mirror of
https://github.com/doocs/md.git
synced 2024-11-24 19:10:34 +08:00
add: add right-side menu
This commit is contained in:
parent
67c38d8127
commit
326192edf0
@ -213,4 +213,17 @@ export function fixCodeWhiteSpace(value = 'pre') {
|
||||
pre.style.whiteSpace = value;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function downLoadMD(doc) {
|
||||
let downLink = document.createElement('a');
|
||||
|
||||
downLink.download = 'content.md';
|
||||
downLink.style.display = 'none';
|
||||
let blob = new Blob([doc]);
|
||||
|
||||
downLink.href = URL.createObjectURL(blob);
|
||||
document.body.appendChild(downLink);
|
||||
downLink.click();
|
||||
document.body.removeChild(downLink);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-dialog title="关于" class="about__dialog" :visible="aboutDialogVisible" @close="$emit('close')" width="30%" center>
|
||||
<el-dialog title="关于" class="about__dialog" :visible="value" @close="$emit('input', false)" width="30%" center>
|
||||
<div style="text-align: center;">
|
||||
<h3>一款高度简洁的微信 Markdown 编辑器</h3>
|
||||
</div>
|
||||
@ -17,7 +17,7 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
aboutDialogVisible: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
</el-upload>
|
||||
<!-- 下载文本文档 -->
|
||||
<el-tooltip class="header__item" :effect="effect" content="下载编辑框Markdown文档" placement="bottom-start">
|
||||
<i class="el-icon-download" size="medium" @click="downloadEditorContent"></i>
|
||||
<i class="el-icon-download" size="medium" @click="$emit('downLoad')"></i>
|
||||
</el-tooltip>
|
||||
<!-- 页面重置 -->
|
||||
<el-tooltip class="header__item" :effect="effect" content="重置页面" placement="bottom-start">
|
||||
@ -71,10 +71,11 @@
|
||||
<script>
|
||||
|
||||
import {
|
||||
setColorWithCustomTemplate,
|
||||
downLoadMD,
|
||||
setFontSize,
|
||||
isImageIllegal,
|
||||
fixCodeWhiteSpace
|
||||
fixCodeWhiteSpace,
|
||||
setColorWithCustomTemplate
|
||||
} from '../../assets/scripts/util'
|
||||
import fileApi from '../../api/file';
|
||||
import {
|
||||
@ -205,7 +206,7 @@ export default {
|
||||
},
|
||||
// 自定义CSS样式
|
||||
async customStyle () {
|
||||
this.$emit('showBox');
|
||||
this.$emit('showCssEditor');
|
||||
this.$nextTick(() => {
|
||||
if(!this.cssEditor) {
|
||||
this.cssEditor.refresh()
|
||||
@ -240,17 +241,6 @@ export default {
|
||||
this.showResetConfirm = false;
|
||||
this.editor.focus()
|
||||
},
|
||||
// 下载编辑器内容到本地
|
||||
downloadEditorContent () {
|
||||
let downLink = document.createElement('a')
|
||||
downLink.download = 'content.md'
|
||||
downLink.style.display = 'none'
|
||||
let blob = new Blob([this.editor.getValue(0)])
|
||||
downLink.href = URL.createObjectURL(blob)
|
||||
document.body.appendChild(downLink)
|
||||
downLink.click()
|
||||
document.body.removeChild(downLink)
|
||||
},
|
||||
...mapMutations(['clearEditorToDefault','setCurrentColor', 'setCiteStatus', 'themeChanged',
|
||||
'setCurrentFont', 'setCurrentSize', 'setCssEditorValue', 'setWxRendererOptions'])
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-dialog title="插入表格" class="insert__dialog" :visible="dialogFormVisible" @close="$emit('close')">
|
||||
<el-dialog title="插入表格" class="insert__dialog" :visible="value" @close="$emit('input', false)">
|
||||
<el-form class="insert__form" :model="config.form">
|
||||
<el-form-item label="行数(表头不计入行数)">
|
||||
<el-input v-model="config.form.rows"></el-input>
|
||||
@ -9,7 +9,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button :type="btnType" plain @click="$emit('close')">取 消</el-button>
|
||||
<el-button :type="btnType" plain @click="$emit('input', false)">取 消</el-button>
|
||||
<el-button :type="btnType" @click="insertTable" plain>确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
@ -20,7 +20,7 @@ import config from '../../assets/scripts/config'
|
||||
import {mapState, mapMutations} from 'vuex';
|
||||
export default {
|
||||
props: {
|
||||
dialogFormVisible: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
@ -63,7 +63,7 @@ export default {
|
||||
}
|
||||
|
||||
this.editor.replaceSelection(`\n${table}\n`, cursor)
|
||||
this.$emit('close')
|
||||
this.$emit('input', false)
|
||||
this.editorRefresh()
|
||||
},
|
||||
...mapMutations(['editorRefresh'])
|
||||
|
142
src/components/CodemirrorEditor/rightClickMenu.vue
Normal file
142
src/components/CodemirrorEditor/rightClickMenu.vue
Normal file
@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<ul v-show="value" id="menu" class="menu" :style="`left: ${left}px;top: ${top}px;`">
|
||||
<li v-for="item of list" :key="item.key" class="menu_item">
|
||||
<el-upload v-if="item.key === 'insertPic'" action="" class="li__upload"
|
||||
:show-file-list="false" :multiple="true" accept=".jpg,.jpeg,.png,.gif" name="file"
|
||||
:before-upload="beforeUpload">
|
||||
<span>{{item.text}}</span>
|
||||
</el-upload>
|
||||
<span v-else @click="$emit('menuTick', item.key)">{{item.text}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
isImageIllegal,
|
||||
} from '../../assets/scripts/util';
|
||||
import fileApi from '../../api/file';
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
top: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
left: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: [
|
||||
{
|
||||
text: '上传图片',
|
||||
key: 'insertPic'
|
||||
},
|
||||
{
|
||||
text: '插入表格',
|
||||
key: 'insertTable'
|
||||
},
|
||||
{
|
||||
text: '页面重置',
|
||||
key: 'pageReset'
|
||||
},
|
||||
{
|
||||
text: '下载MD文档',
|
||||
key: 'downLoad'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeCB() {
|
||||
this.$emit('input', false);
|
||||
},
|
||||
// 空函数,阻断el-upload上传
|
||||
// 图片上传前的处理
|
||||
beforeUpload(file) {
|
||||
const checkImageResult = isImageIllegal(file);
|
||||
|
||||
if (checkImageResult) {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: checkImageResult,
|
||||
type: 'error'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
let fd = new FormData();
|
||||
|
||||
fd.append('file', file);
|
||||
fileApi.fileUpload(fd).then(res => {
|
||||
this.$emit('menuTick', 'insertPic', res)
|
||||
}).catch(err => {
|
||||
console.log(err.message)
|
||||
})
|
||||
return false;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
if (newVal) {
|
||||
document.body.addEventListener('click', this.closeCB.bind(this));
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.closeCB.bind(this));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.menu {
|
||||
position: absolute;
|
||||
padding: 6px 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #aaaaaa;
|
||||
background-color: #ffffff;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.menu_item {
|
||||
margin-top: 10px;
|
||||
min-width: 125px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #303133;
|
||||
cursor: pointer;
|
||||
&:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
&:hover {
|
||||
color: white;
|
||||
background: rgb(139, 146, 148);
|
||||
}
|
||||
span {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
padding: 4px 0;
|
||||
width: 100%;
|
||||
}
|
||||
/deep/ .el-upload {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
li:hover {
|
||||
background-color: #1790ff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 15px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
</style>
|
@ -3,10 +3,12 @@
|
||||
<el-container>
|
||||
<el-header class="editor__header">
|
||||
<editor-header
|
||||
ref="header"
|
||||
@refresh="onEditorRefresh"
|
||||
@uploaded="uploaded"
|
||||
@cssChanged="cssChanged"
|
||||
@showBox="showBox = !showBox"
|
||||
@downLoad="downloadEditorContent"
|
||||
@showCssEditor="showCssEditor = !showCssEditor"
|
||||
@showAboutDialog="aboutDialogVisible = true"
|
||||
@showDialogForm="dialogFormVisible = true"
|
||||
@startCopy="isCoping = true, backLight = true"
|
||||
@ -15,7 +17,9 @@
|
||||
</el-header>
|
||||
<el-main class="main-body">
|
||||
<el-row class="main-section">
|
||||
<el-col :span="12">
|
||||
<el-col :span="12"
|
||||
@contextmenu.prevent.native="openMenu($event)"
|
||||
>
|
||||
<textarea id="editor" type="textarea" placeholder="Your markdown text here." v-model="source">
|
||||
</textarea>
|
||||
</el-col>
|
||||
@ -38,7 +42,7 @@
|
||||
</section>
|
||||
</el-col>
|
||||
<transition name="custom-classes-transition" enter-active-class="bounceInRight">
|
||||
<el-col id="cssBox" :span="12" v-show="showBox">
|
||||
<el-col id="cssBox" :span="12" v-show="showCssEditor">
|
||||
<textarea id="cssEditor" type="textarea" placeholder="Your custom css here.">
|
||||
</textarea>
|
||||
</el-col>
|
||||
@ -46,10 +50,16 @@
|
||||
</el-row>
|
||||
</el-main>
|
||||
</el-container>
|
||||
<about-dialog :aboutDialogVisible="aboutDialogVisible"
|
||||
@close="aboutDialogVisible = false" />
|
||||
<insert-form-dialog :dialogFormVisible="dialogFormVisible"
|
||||
@close="dialogFormVisible = false" />
|
||||
<about-dialog v-model="aboutDialogVisible"/>
|
||||
<insert-form-dialog v-model="dialogFormVisible"/>
|
||||
<right-click-menu
|
||||
v-model="rightClickMenuVisible"
|
||||
:left="mouseLeft"
|
||||
:top="mouseTop"
|
||||
@downLoad="downloadEditorContent"
|
||||
@menuTick="onMenuEvent"
|
||||
@insertTable="dialogFormVisible = true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@ -57,12 +67,14 @@ import fileApi from '../api/file';
|
||||
import editorHeader from '../components/CodemirrorEditor/header';
|
||||
import aboutDialog from '../components/CodemirrorEditor/aboutDialog';
|
||||
import insertFormDialog from '../components/CodemirrorEditor/insertForm';
|
||||
import rightClickMenu from '../components/CodemirrorEditor/rightClickMenu';
|
||||
import {
|
||||
setFontSize,
|
||||
css2json,
|
||||
customCssWithTemplate,
|
||||
downLoadMD,
|
||||
setFontSize,
|
||||
isImageIllegal,
|
||||
saveEditorContent,
|
||||
isImageIllegal
|
||||
customCssWithTemplate
|
||||
} from '../assets/scripts/util'
|
||||
|
||||
require('codemirror/mode/javascript/javascript')
|
||||
@ -70,18 +82,24 @@ import {mapState, mapMutations} from 'vuex';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
showBox: false,
|
||||
showCssEditor: false,
|
||||
aboutDialogVisible: false,
|
||||
dialogFormVisible: false,
|
||||
rightClickMenuVisible: false,
|
||||
isCoping: false,
|
||||
backLight: false,
|
||||
timeout: null,
|
||||
changeTimer: null,
|
||||
source: ''
|
||||
source: '',
|
||||
mouseLeft: 0,
|
||||
mouseTop: 0
|
||||
}
|
||||
},
|
||||
components: {
|
||||
editorHeader, aboutDialog, insertFormDialog
|
||||
editorHeader,
|
||||
aboutDialog,
|
||||
insertFormDialog,
|
||||
rightClickMenu
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
@ -153,52 +171,53 @@ export default {
|
||||
}
|
||||
});
|
||||
this.cssEditor.on('update', (instance) => {
|
||||
this.cssChanged()
|
||||
this.cssChanged();
|
||||
saveEditorContent(this.cssEditor, '__css_content')
|
||||
})
|
||||
},
|
||||
cssChanged() {
|
||||
let json = css2json(this.cssEditor.getValue(0))
|
||||
let theme = setFontSize(this.currentSize.replace('px', ''))
|
||||
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.onEditorRefresh()
|
||||
this.onEditorRefresh();
|
||||
},
|
||||
// 图片上传结束
|
||||
uploaded(response, file, fileList) {
|
||||
uploaded(response) {
|
||||
if (response) {
|
||||
if (response.success) {
|
||||
// 上传成功,获取光标
|
||||
const cursor = this.editor.getCursor()
|
||||
const imageUrl = response.data
|
||||
const markdownImage = `![](${imageUrl})`
|
||||
const cursor = this.editor.getCursor();
|
||||
const imageUrl = response.data;
|
||||
const markdownImage = `![](${imageUrl})`;
|
||||
// 将 Markdown 形式的 URL 插入编辑框光标所在位置
|
||||
this.editor.replaceSelection(`\n${markdownImage}\n`, cursor)
|
||||
this.editor.replaceSelection(`\n${markdownImage}\n`, cursor);
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '图片插入成功',
|
||||
type: 'success'
|
||||
})
|
||||
this.onEditorRefresh()
|
||||
});
|
||||
this.onEditorRefresh();
|
||||
} else {
|
||||
// 上传失败
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: response.message,
|
||||
type: 'error'
|
||||
})
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '上传图片未知异常',
|
||||
type: 'error'
|
||||
})
|
||||
});
|
||||
}
|
||||
},
|
||||
// 左右滚动
|
||||
leftAndRightScroll() {
|
||||
const scrollCB = text=> {
|
||||
let source, target;
|
||||
@ -235,16 +254,51 @@ export default {
|
||||
this.$refs.preview.$el.addEventListener("scroll", previewScrollCB, false);
|
||||
this.editor.on('scroll', editorScrollCB);
|
||||
},
|
||||
// 更新编辑器
|
||||
onEditorRefresh() {
|
||||
this.editorRefresh();
|
||||
setTimeout(()=> PR.prettyPrint(), 0);
|
||||
},
|
||||
// 复制结束
|
||||
endCopy() {
|
||||
this.backLight = false;
|
||||
setTimeout(()=> {
|
||||
this.isCoping = false;
|
||||
}, 800);
|
||||
},
|
||||
// 下载编辑器内容到本地
|
||||
downloadEditorContent() {
|
||||
downLoadMD(this.editor.getValue(0));
|
||||
},
|
||||
// 右键菜单
|
||||
openMenu(e) {
|
||||
const menuMinWidth = 105;
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left;
|
||||
const offsetWidth = this.$el.offsetWidth;
|
||||
const maxLeft = offsetWidth - menuMinWidth;
|
||||
const left = e.clientX - offsetLeft;
|
||||
|
||||
if (left > maxLeft) {
|
||||
this.mouseLeft = maxLeft;
|
||||
} else {
|
||||
this.mouseLeft = left;
|
||||
}
|
||||
|
||||
this.mouseTop = e.clientY + 10;
|
||||
this.rightClickMenuVisible = true;
|
||||
},
|
||||
onMenuEvent(type, info = {}) {
|
||||
switch (type) {
|
||||
case 'pageReset':
|
||||
this.$refs.header.showResetConfirm = true;
|
||||
break;
|
||||
case 'insertPic':
|
||||
this.uploaded(info);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
...mapMutations([
|
||||
'initEditorState',
|
||||
'initEditorEntity',
|
||||
|
Loading…
Reference in New Issue
Block a user