add: add right-side menu

This commit is contained in:
JimQing 2020-07-13 00:26:29 +08:00
parent 67c38d8127
commit 326192edf0
6 changed files with 246 additions and 47 deletions

View File

@ -214,3 +214,16 @@ export function fixCodeWhiteSpace(value = 'pre') {
})
}
}
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);
}

View File

@ -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
}

View File

@ -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'])
},

View File

@ -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'])

View 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>

View File

@ -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',