split component and bug fix

This commit is contained in:
JimQing 2020-05-02 11:50:26 +08:00
parent e68bda4ab6
commit 9a42d070eb
7 changed files with 393 additions and 345 deletions

View File

@ -11,7 +11,6 @@ service.interceptors.request.use(
if (/^(post)|(put)|(delete)$/i.test(config.method)) { if (/^(post)|(put)|(delete)$/i.test(config.method)) {
if (config.data && config.data.upload) { if (config.data && config.data.upload) {
config.headers['Content-Type'] = 'multipart/form-data'; config.headers['Content-Type'] = 'multipart/form-data';
config.headers['Access-Control-Allow-Origin'] = 'http://192.168.0.106:8080';
} }
} }
return config; return config;

View File

@ -1,123 +1,41 @@
<template> <template>
<div id="app" class="container"> <div id="app" class="container">
<el-container> <el-container>
<el-header class="top"> <el-header class="top editor__header">
<!-- 图片上传 --> <editor-header
<el-upload class="header__item" action="https://imgkr.com/api/files/upload" :headers="{'Content-Type': 'multipart/form-data'}" @showBox="showBox = !showBox"
:show-file-list="false" :multiple="true" accept=".jpg,.jpeg,.png,.gif" name="file" @showAboutDialog="aboutDialogVisible = true"
:before-upload="beforeUpload" :on-success="uploaded"> @showDialogForm="dialogFormVisible = true"
<el-tooltip effect="dark" content="上传图片" placement="bottom-start"> />
<i class="el-icon-upload" size="medium">&nbsp;</i> </el-header>
</el-tooltip> <el-main class="main-body">
</el-upload> <el-row :gutter="10" class="main-section">
<!-- 下载文本文档 --> <el-col :span="12">
<el-tooltip class="header__item" effect="dark" content="下载编辑框Markdown文档" placement="bottom-start"> <textarea id="editor" type="textarea" placeholder="Your markdown text here." v-model="source">
<i class="el-icon-download" size="medium" @click="downloadEditorContent">&nbsp;</i> </textarea>
</el-tooltip> </el-col>
<!-- 页面重置 --> <el-col :span="12" class="preview-wrapper" id="preview">
<el-tooltip class="header__item" effect="dark" content="重置页面" placement="bottom-start"> <section id="output-wrapper">
<i class="el-icon-refresh" size="medium" @click="reset">&nbsp;</i> <div class="preview" contenteditable="true">
</el-tooltip> <section id="output" v-html="output">
<!-- 插入表格 --> </section>
<el-tooltip class="header__item header__item_last" effect="dark" content="插入表格" placement="bottom-start"> </div>
<i class="el-icon-s-grid" size="medium" @click="dialogFormVisible = true">&nbsp;</i> </section>
</el-tooltip> </el-col>
<el-form size="mini" class="ctrl" :inline=true> <transition name="custom-classes-transition" enter-active-class="animated bounceInRight">
<el-form-item> <el-col id="cssBox" :span="12" v-show="showBox">
<el-select v-model="selectFont" size="mini" placeholder="选择字体" clearable @change="fontChanged"> <textarea id="cssEditor" type="textarea" placeholder="Your custom css here.">
<el-option v-for="font in config.builtinFonts" :style="{fontFamily: font.value}" :key="font.value" </textarea>
:label="font.label" :value="font.value"> </el-col>
<span class="select-item-left">{{ font.label }}</span> </transition>
<span class="select-item-right">Abc</span> </el-row>
</el-option> </el-main>
</el-select> </el-container>
</el-form-item> <about-dialog :aboutDialogVisible="aboutDialogVisible"
<el-form-item> @close="aboutDialogVisible = false" />
<el-select v-model="selectSize" size="mini" placeholder="选择段落字号" clearable @change="sizeChanged"> <insert-form-dialog :dialogFormVisible="dialogFormVisible"
<el-option v-for="size in config.sizeOption" :key="size.value" :label="size.label" :value="size.value"> @close="dialogFormVisible = false" />
<span class="select-item-left">{{ size.label }}</span> </div>
<span class="select-item-right">{{ size.desc }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="selectColor" size="mini" placeholder="选择颜色" clearable @change="colorChanged">
<el-option v-for="color in config.colorOption" :key="color.value" :label="color.label" :value="color.value">
<span class="select-item-left">{{ color.label }}</span>
<span class="select-item-right">{{ color.hex }}</span>
</el-option>
</el-select>
</el-form-item>
<el-tooltip content="自定义颜色" placement="top">
<el-color-picker v-model="selectColor" size="mini" show-alpha @change="colorChanged"></el-color-picker>
</el-tooltip>
<el-tooltip content="微信外链自动转为文末引用" placement="top">
<el-switch class="header__switch" v-model="status" active-color="#67c23a" inactive-color="#dcdfe6" @change="statusChanged">
</el-switch>
</el-tooltip>
</el-form>
<el-tooltip class="item" effect="dark" content="自定义CSS样式" placement="left">
<el-button type="success" plain size="medium" icon="el-icon-setting" @click="customStyle"></el-button>
</el-tooltip>
<el-button type="success" plain size="medium" @click="copy">复制</el-button>
<el-button type="success" plain size="medium" class="about" @click="aboutDialogVisible = true">关于</el-button>
</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>
<el-dialog title="关于" :visible.sync="aboutDialogVisible" width="30%" center>
<div style="text-align: center;">
<h3>一款高度简洁的微信 Markdown 编辑器</h3>
</div>
<div style="text-align: center;margin-top:10px;">
<p>扫码关注我的公众号原创技术文章第一时间推送</p>
<img src="assets/images/qrcode-for-doocs.jpg" style="width: 40%; display: block; margin: 20px auto 10px;">
</div>
<span slot="footer" class="dialog-footer">
<a href="https://github.com/doocs/md" target="_blank">
<el-button type="success" plain>GitHub 仓库</el-button>
</a>
<a href="https://gitee.com/doocs/md" target="_blank">
<el-button type="success" plain>Gitee 仓库</el-button>
</a>
</span>
</el-dialog>
<el-dialog title="插入表格" :visible.sync="dialogFormVisible">
<el-form :model="config.form">
<el-form-item label="行数(表头不计入行数)">
<el-input v-model="config.form.rows"></el-input>
</el-form-item>
<el-form-item label="列数">
<el-input v-model="config.form.cols"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="success" plain @click="dialogFormVisible = false"> </el-button>
<el-button type="success" @click="insertTable"> </el-button>
</div>
</el-dialog>
</div>
</template> </template>
<script> <script>
import CodeMirror from 'codemirror/lib/codemirror' import CodeMirror from 'codemirror/lib/codemirror'
@ -132,48 +50,32 @@
import '../scripts/format.js' import '../scripts/format.js'
import fileApi from '../api/file'; import fileApi from '../api/file';
import marked from 'marked' import editorHeader from './codeMirror/header';
import markdown from 'markdown' import aboutDialog from './codeMirror/aboutDialog';
import juice from 'juice' import insertFormDialog from './codeMirror/insertForm';
import EditorHeader from './codeMirror/header';
import { import {
setColorWithCustomTemplate,
setColor,
setFontSize,
css2json,
customCssWithTemplate,
saveEditorContent, saveEditorContent,
checkImage isImageIllegal
} from '../scripts/util' } from '../scripts/util'
import DEFAULT_CSS_CONTENT from '../scripts/themes/default-theme-css'
require('codemirror/mode/javascript/javascript') require('codemirror/mode/javascript/javascript')
import '../scripts/closebrackets' import '../scripts/closebrackets'
import $ from 'jquery' import $ from 'jquery'
import {
solveWeChatImage,
solveHtml,
copySafari
} from '../scripts/converter'
import config from '../scripts/config' import config from '../scripts/config'
import {mapState, mapMutations} from 'vuex'; import {mapState, mapMutations} from 'vuex';
export default { export default {
data() { data() {
return { return {
config: config, config: config,
showBox: false, showBox: false,
aboutDialogVisible: false, aboutDialogVisible: false,
dialogFormVisible: false, dialogFormVisible: false,
timeout: null, timeout: null,
source: '', source: ''
selectFont: '', }
selectSize: '',
selectColor: '',
status: '1'
}
}, },
components: { components: {
EditorHeader editorHeader, aboutDialog, insertFormDialog
}, },
computed: { computed: {
...mapState({ ...mapState({
@ -181,10 +83,7 @@
output: state=> state.output, output: state=> state.output,
editor: state=> state.editor, editor: state=> state.editor,
cssEditor: state=> state.cssEditor, cssEditor: state=> state.cssEditor,
html: state=> state.html, html: state=> state.html
currentFont: state=> state.currentFont,
currentSize: state=> state.currentSize,
currentColor: state=> state.currentColor
}) })
}, },
created() { created() {
@ -194,9 +93,6 @@
this.initCssEditor() this.initCssEditor()
this.editorRefresh() this.editorRefresh()
}) })
this.selectFont = this.currentFont
this.selectSize = this.currentSize
this.selectColor = this.currentColor
}, },
methods: { methods: {
initEditor() { initEditor() {
@ -215,7 +111,7 @@
let item = e.clipboardData.items[i] let item = e.clipboardData.items[i]
if (item.kind === 'file') { if (item.kind === 'file') {
const pasteFile = item.getAsFile() const pasteFile = item.getAsFile()
const checkImageResult = checkImage(pasteFile); const checkImageResult = isImageIllegal(pasteFile);
if (checkImageResult) { if (checkImageResult) {
this.$message({ this.$message({
@ -250,29 +146,6 @@
saveEditorContent(this.cssEditor, '__css_content') saveEditorContent(this.cssEditor, '__css_content')
}) })
}, },
//
beforeUpload(file) {
const checkImageResult = checkImage(file);
if (checkImageResult) {
this.$message({
showClose: true,
message: checkImageResult,
type: 'error'
});
return false;
}
return true;
},
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()
},
// //
uploaded(response, file, fileList) { uploaded(response, file, fileList) {
if (response.success) { if (response.success) {
@ -297,32 +170,6 @@
}) })
} }
}, },
//
insertTable() {
const cursor = this.editor.getCursor()
const rows = parseInt(this.config.form.rows)
const cols = parseInt(this.config.form.cols)
if (isNaN(rows) || isNaN(cols) || rows < 1 || cols < 1) {
this.$message({
showClose: true,
message: '输入的行/列数无效,请重新输入',
type: 'error'
})
return
}
let table = ''
for (let i = 0; i < rows + 2; ++i) {
for (let j = 0; j < cols + 1; ++j) {
table += (j === 0 ? '|' : (i !== 1 ? ' |' : ' --- |'))
}
table += '\n'
}
this.editor.replaceSelection(`\n${table}\n`, cursor)
this.dialogFormVisible = false
this.editorRefresh()
},
// //
copy12() { copy12() {
let clipboardDiv = document.getElementById('output') let clipboardDiv = document.getElementById('output')
@ -344,22 +191,6 @@
type: 'success' type: 'success'
}) })
}, },
//
copy() {
let clipboardDiv = document.getElementById('output')
const clipboardHTML = clipboardDiv.innerHTML
// solveWeChatImage()
this.html = solveHtml();
//
this.$notify({
showClose: true,
message: '已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴',
offset: 80,
duration: 1600,
type: 'success'
})
clipboardDiv.innerHTML = clipboardHTML; //
},
// //
leftAndRightScroll() { leftAndRightScroll() {
$('div.CodeMirror-scroll, #preview').on('scroll', function callback() { $('div.CodeMirror-scroll, #preview').on('scroll', function callback() {
@ -382,104 +213,16 @@
}, 100) }, 100)
}) })
}, },
// ...mapMutations(['initEditorState', 'initEditorEntity', 'editorRefresh', 'initCssEditorEntity'])
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)
},
//
reset() {
this.$confirm('此操作将丢失本地缓存的文本和自定义样式,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
confirmButtonClass: 'el-button--success',
cancelButtonClass: 'el-button--success is-plain',
type: 'warning',
center: true
}).then(() => {
localStorage.clear()
this.clearEditorToDefault();
this.editor.focus()
this.status = '1';
this.fontChanged(this.config.builtinFonts[0].value)
this.colorChanged(this.config.colorOption[1].value)
this.sizeChanged(this.config.sizeOption[2].value)
this.cssChanged()
}).catch(() => {
this.editor.focus()
})
},
fontChanged(fonts) {
this.setWxRendererOptions({
fonts: fonts
})
this.setCurrentFont(fonts);
this.editorRefresh()
},
sizeChanged(size) {
let theme = setFontSize(size.replace('px', ''))
theme = setColorWithCustomTemplate(theme, this.currentColor)
this.setWxRendererOptions({
size: size,
theme: theme
})
this.setCurrentSize(size);
this.editorRefresh()
},
colorChanged(color) {
let theme = setFontSize(this.currentSize.replace('px', ''))
theme = setColorWithCustomTemplate(theme, color)
this.setWxRendererOptions({
theme: theme
})
this.setCurrentColor(color);
this.editorRefresh()
},
statusChanged () {
localStorage.setItem('status', this.status)
this.editorRefresh()
},
// CSS
async customStyle () {
this.showBox = !this.showBox
this.$nextTick(() => {
if(!this.cssEditor) {
this.cssEditor.refresh()
// this.initCssEditor()
}
})
setTimeout(() => {
this.cssEditor.refresh()
},50)
let flag = await localStorage.getItem('__css_content')
if (!flag) {
this.setCssEditorValue(DEFAULT_CSS_CONTENT)
}
},
...mapMutations(['initEditorState', 'initEditorEntity', 'editorRefresh', 'clearEditorToDefault',
'setCurrentFont', 'setCurrentSize', 'setCurrentColor', 'setEditorValue', 'setCssEditorValue',
'initCssEditorEntity', 'setWxRendererOptions'])
}, },
mounted() { mounted() {
this.leftAndRightScroll() this.leftAndRightScroll()
} }
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.header__item { .main-body {
margin: 0 3px; padding-top: 0;
}
.header__item_last {
margin-right: 8px;
}
.header__switch {
margin-left: 8px;
} }
</style> </style>

View File

@ -0,0 +1,34 @@
<template>
<el-dialog title="关于" :visible="aboutDialogVisible" @close="$emit('close')" width="30%" center>
<div style="text-align: center;">
<h3>一款高度简洁的微信 Markdown 编辑器</h3>
</div>
<div style="text-align: center;margin-top:10px;">
<p>扫码关注我的公众号原创技术文章第一时间推送</p>
<img src="assets/images/qrcode-for-doocs.jpg" style="width: 40%; display: block; margin: 20px auto 10px;">
</div>
<span slot="footer" class="dialog-footer">
<el-button type="success" @click="onRedirect('https://github.com/doocs/md')" plain>GitHub 仓库</el-button>
<el-button type="success" @click="onRedirect('https://gitee.com/doocs/md')" plain>Gitee 仓库</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
props: {
aboutDialogVisible: {
type: Boolean,
default: false
}
},
methods: {
onRedirect(url) {
window.open(url)
}
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -1,29 +1,29 @@
<template> <template>
<div> <el-container class="top">
<!-- 图片上传 --> <!-- 图片上传 -->
<el-upload action="https://imgkr.com/api/files/upload" :headers="{'Content-Type': 'multipart/form-data'}" <el-upload class="header__item" action="https://imgkr.com/api/files/upload" :headers="{'Content-Type': 'multipart/form-data'}"
:show-file-list="false" :multiple="true" accept=".jpg,.jpeg,.png,.gif" name="file" :show-file-list="false" :multiple="true" accept=".jpg,.jpeg,.png,.gif" name="file"
:before-upload="beforeUpload" :on-success="uploaded"> :before-upload="beforeUpload" :on-success="uploaded">
<el-tooltip class="item" effect="dark" content="上传图片" placement="bottom-start"> <el-tooltip effect="dark" content="上传图片" placement="bottom-start">
<i class="el-icon-upload" size="medium">&nbsp;</i> <i class="el-icon-upload" size="medium"></i>
</el-tooltip> </el-tooltip>
</el-upload> </el-upload>
<!-- 下载文本文档 --> <!-- 下载文本文档 -->
<el-tooltip class="item" effect="dark" content="下载编辑框Markdown文档" placement="bottom-start"> <el-tooltip class="header__item" effect="dark" content="下载编辑框Markdown文档" placement="bottom-start">
<i class="el-icon-download" size="medium" @click="downloadEditorContent">&nbsp;</i> <i class="el-icon-download" size="medium" @click="downloadEditorContent"></i>
</el-tooltip> </el-tooltip>
<!-- 页面重置 --> <!-- 页面重置 -->
<el-tooltip class="item" effect="dark" content="重置页面" placement="bottom-start"> <el-tooltip class="header__item" effect="dark" content="重置页面" placement="bottom-start">
<i class="el-icon-refresh" size="medium" @click="reset">&nbsp;</i> <i class="el-icon-refresh" size="medium" @click="reset"></i>
</el-tooltip> </el-tooltip>
<!-- 插入表格 --> <!-- 插入表格 -->
<el-tooltip class="item" effect="dark" content="插入表格" placement="bottom-start"> <el-tooltip class="header__item header__item_last" effect="dark" content="插入表格" placement="bottom-start">
<i class="el-icon-s-grid" size="medium" @click="dialogFormVisible = true">&nbsp;</i> <i class="el-icon-s-grid" size="medium" @click="$emit('showDialogForm')"></i>
</el-tooltip> </el-tooltip>
<el-form size="mini" class="ctrl" :inline=true> <el-form size="mini" class="ctrl" :inline=true>
<el-form-item> <el-form-item>
<el-select v-model="currentFont" size="mini" placeholder="选择字体" clearable @change="fontChanged"> <el-select v-model="selectFont" size="mini" placeholder="选择字体" clearable @change="fontChanged">
<el-option v-for="font in builtinFonts" :style="{fontFamily: font.value}" :key="font.value" <el-option v-for="font in config.builtinFonts" :style="{fontFamily: font.value}" :key="font.value"
:label="font.label" :value="font.value"> :label="font.label" :value="font.value">
<span class="select-item-left">{{ font.label }}</span> <span class="select-item-left">{{ font.label }}</span>
<span class="select-item-right">Abc</span> <span class="select-item-right">Abc</span>
@ -31,27 +31,26 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-select v-model="currentSize" size="mini" placeholder="选择段落字号" clearable @change="sizeChanged"> <el-select v-model="selectSize" size="mini" placeholder="选择段落字号" clearable @change="sizeChanged">
<el-option v-for="size in sizeOption" :key="size.value" :label="size.label" :value="size.value"> <el-option v-for="size in config.sizeOption" :key="size.value" :label="size.label" :value="size.value">
<span class="select-item-left">{{ size.label }}</span> <span class="select-item-left">{{ size.label }}</span>
<span class="select-item-right">{{ size.desc }}</span> <span class="select-item-right">{{ size.desc }}</span>
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-select v-model="currentColor" size="mini" placeholder="选择颜色" clearable @change="colorChanged"> <el-select v-model="selectColor" size="mini" placeholder="选择颜色" clearable @change="colorChanged">
<el-option v-for="color in colorOption" :key="color.value" :label="color.label" :value="color.value"> <el-option v-for="color in config.colorOption" :key="color.value" :label="color.label" :value="color.value">
<span class="select-item-left">{{ color.label }}</span> <span class="select-item-left">{{ color.label }}</span>
<span class="select-item-right">{{ color.hex }}</span> <span class="select-item-right">{{ color.hex }}</span>
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-tooltip content="自定义颜色" placement="top"> <el-tooltip content="自定义颜色" placement="top">
<el-color-picker v-model="currentColor" size="mini" show-alpha @change="colorChanged"></el-color-picker> <el-color-picker v-model="selectColor" size="mini" show-alpha @change="colorChanged"></el-color-picker>
</el-tooltip> </el-tooltip>
&nbsp;&nbsp;
<el-tooltip content="微信外链自动转为文末引用" placement="top"> <el-tooltip content="微信外链自动转为文末引用" placement="top">
<el-switch v-model="status" active-color="#67c23a" inactive-color="#dcdfe6" @change="statusChanged"> <el-switch class="header__switch" v-model="citeStatus" active-color="#67c23a" inactive-color="#dcdfe6" @change="statusChanged">
</el-switch> </el-switch>
</el-tooltip> </el-tooltip>
</el-form> </el-form>
@ -59,19 +58,215 @@
<el-button type="success" plain size="medium" icon="el-icon-setting" @click="customStyle"></el-button> <el-button type="success" plain size="medium" icon="el-icon-setting" @click="customStyle"></el-button>
</el-tooltip> </el-tooltip>
<el-button type="success" plain size="medium" @click="copy">复制</el-button> <el-button type="success" plain size="medium" @click="copy">复制</el-button>
<el-button type="success" plain size="medium" class="about" @click="aboutDialogVisible = true">关于</el-button> <el-button type="success" plain size="medium" class="about" @click="$emit('showAboutDialog')">关于</el-button>
</div> </el-container>
</template> </template>
<script> <script>
export default {
name: 'header',
methods: {
import {
setColorWithCustomTemplate,
setFontSize,
css2json,
isImageIllegal,
customCssWithTemplate,
} from '../../scripts/util'
import {
solveWeChatImage,
solveHtml
} from '../../scripts/converter'
import config from '../../scripts/config'
import DEFAULT_CSS_CONTENT from '../../scripts/themes/default-theme-css'
import {mapState, mapMutations} from 'vuex'
export default {
name: 'editor-header',
data() {
return {
config: config,
citeStatus: false,
selectFont: '',
selectSize: '',
selectColor: ''
};
}, },
computed: {
...mapState({
output: state=> state.output,
editor: state=> state.editor,
cssEditor: state=> state.cssEditor,
html: state=> state.html,
currentFont: state=> state.currentFont,
currentSize: state=> state.currentSize,
currentColor: state=> state.currentColor
})
},
methods: {
fontChanged(fonts) {
this.setWxRendererOptions({
fonts: fonts
})
this.setCurrentFont(fonts);
this.editorRefresh()
},
sizeChanged(size) {
let theme = setFontSize(size.replace('px', ''))
theme = setColorWithCustomTemplate(theme, this.currentColor)
this.setWxRendererOptions({
size: size,
theme: theme
})
this.setCurrentSize(size);
this.editorRefresh()
},
colorChanged(color) {
let theme = setFontSize(this.currentSize.replace('px', ''))
theme = setColorWithCustomTemplate(theme, color)
this.setWxRendererOptions({
theme: theme
})
this.setCurrentColor(color);
this.editorRefresh()
},
statusChanged(val) {
this.setCiteStatus(val)
this.editorRefresh()
},
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()
},
//
uploaded(response, file, fileList) {
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'
})
}
},
//
beforeUpload(file) {
const checkImageResult = isImageIllegal(file);
if (checkImageResult) {
this.$message({
showClose: true,
message: checkImageResult,
type: 'error'
});
return false;
}
return true;
},
//
copy() {
let clipboardDiv = document.getElementById('output')
const clipboardHTML = clipboardDiv.innerHTML
// solveWeChatImage()
this.html = solveHtml();
//
this.$notify({
showClose: true,
message: '已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴',
offset: 80,
duration: 1600,
type: 'success'
})
clipboardDiv.innerHTML = clipboardHTML; //
},
// CSS
async customStyle () {
this.$emit('showBox');
this.$nextTick(() => {
if(!this.cssEditor) {
this.cssEditor.refresh()
}
})
setTimeout(() => {
this.cssEditor.refresh()
},50)
let flag = await localStorage.getItem('__css_content')
if (!flag) {
this.setCssEditorValue(DEFAULT_CSS_CONTENT)
}
},
//
reset() {
this.$confirm('此操作将丢失本地缓存的文本和自定义样式,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
confirmButtonClass: 'el-button--success',
cancelButtonClass: 'el-button--success is-plain',
type: 'warning',
center: true
}).then(() => {
localStorage.clear()
this.clearEditorToDefault();
this.editor.focus()
this.citeStatus = false;
this.statusChanged(false);
this.fontChanged(this.config.builtinFonts[0].value)
this.colorChanged(this.config.colorOption[1].value)
this.sizeChanged(this.config.sizeOption[2].value)
this.cssChanged()
}).catch(() => {
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(['editorRefresh', 'clearEditorToDefault','setCurrentColor', 'setCiteStatus',
'setCurrentFont', 'setCurrentSize', 'setCssEditorValue', 'setWxRendererOptions'])
},
mounted() {
this.selectFont = this.currentFont
this.selectSize = this.currentSize
this.selectColor = this.currentColor
}
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.editor__header {
width: 100%;
}
.header__item {
margin: 0 3px;
}
.header__item_last {
margin-right: 8px;
}
.header__switch {
margin-left: 8px;
}
</style> </style>

View File

@ -0,0 +1,71 @@
<template>
<el-dialog title="插入表格" :visible="dialogFormVisible" @close="$emit('close')">
<el-form :model="config.form">
<el-form-item label="行数(表头不计入行数)">
<el-input v-model="config.form.rows"></el-input>
</el-form-item>
<el-form-item label="列数">
<el-input v-model="config.form.cols"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="success" plain @click="$emit('close')"> </el-button>
<el-button type="success" @click="insertTable"> </el-button>
</div>
</el-dialog>
</template>
<script>
import config from '../../scripts/config'
import {mapState, mapMutations} from 'vuex';
export default {
props: {
dialogFormVisible: {
type: Boolean,
default: false
}
},
data() {
return {
config: config
}
},
computed: {
...mapState({
editor: state=> state.editor
})
},
methods: {
//
insertTable() {
const cursor = this.editor.getCursor()
const rows = parseInt(this.config.form.rows)
const cols = parseInt(this.config.form.cols)
if (isNaN(rows) || isNaN(cols) || rows < 1 || cols < 1) {
this.$message({
showClose: true,
message: '输入的行/列数无效,请重新输入',
type: 'error'
})
return
}
let table = ''
for (let i = 0; i < rows + 2; ++i) {
for (let j = 0; j < cols + 1; ++j) {
table += (j === 0 ? '|' : (i !== 1 ? ' |' : ' --- |'))
}
table += '\n'
}
this.editor.replaceSelection(`\n${table}\n`, cursor)
this.$emit('close')
this.editorRefresh()
},
...mapMutations(['editorRefresh'])
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -184,7 +184,7 @@ export function saveEditorContent(editor, name) {
} }
} }
export function checkImage(file) { export function isImageIllegal(file) {
if (!/\.(gif|jpg|jpeg|png|GIF|JPG|PNG)$/.test(file.name)) { if (!/\.(gif|jpg|jpeg|png|GIF|JPG|PNG)$/.test(file.name)) {
return '请上传 JPG/PNG/GIF 格式的图片'; return '请上传 JPG/PNG/GIF 格式的图片';
} }

View File

@ -20,7 +20,8 @@ const state = {
html: '', html: '',
currentFont: '', currentFont: '',
currentSize: '', currentSize: '',
currentColor: '' currentColor: '',
citeStatus: 0
}; };
const mutations = { const mutations = {
setEditorValue(state, data) { setEditorValue(state, data) {
@ -32,6 +33,10 @@ const mutations = {
setWxRendererOptions(state, data) { setWxRendererOptions(state, data) {
state.wxRenderer.setOptions(data); state.wxRenderer.setOptions(data);
}, },
setCiteStatus(state, data) {
state.citeStatus = data;
localStorage.setItem('citeStatus', data)
},
setCurrentFont(state, data) { setCurrentFont(state, data) {
state.currentFont = data; state.currentFont = data;
localStorage.setItem('fonts', data) localStorage.setItem('fonts', data)
@ -48,12 +53,12 @@ const mutations = {
state.currentFont = localStorage.getItem('fonts') || config.builtinFonts[0].value state.currentFont = localStorage.getItem('fonts') || config.builtinFonts[0].value
state.currentColor = localStorage.getItem('color') || config.colorOption[1].value state.currentColor = localStorage.getItem('color') || config.colorOption[1].value
state.currentSize = localStorage.getItem('size') || config.sizeOption[2].value state.currentSize = localStorage.getItem('size') || config.sizeOption[2].value
state.status = localStorage.getItem('status') === 'true' state.citeStatus = localStorage.getItem('citeStatus') === 'true'
state.wxRenderer = new WxRenderer({ state.wxRenderer = new WxRenderer({
theme: setColor(state.currentColor), theme: setColor(state.currentColor),
fonts: state.currentFont, fonts: state.currentFont,
size: state.currentSize, size: state.currentSize,
status: state.status status: state.citeStatus
}) })
}, },
initEditorEntity(state) { initEditorEntity(state) {
@ -89,6 +94,7 @@ const mutations = {
extraKeys: { extraKeys: {
'Ctrl-F': function autoFormat(editor) { 'Ctrl-F': function autoFormat(editor) {
const totalLines = editor.lineCount() const totalLines = editor.lineCount()
editor.autoFormatRange({ editor.autoFormatRange({
line: 0, line: 0,
ch: 0 ch: 0
@ -109,11 +115,11 @@ const mutations = {
}, },
editorRefresh(state) { editorRefresh(state) {
let output = marked(state.editor.getValue(0), { let output = marked(state.editor.getValue(0), {
renderer: state.wxRenderer.getRenderer(state.status) renderer: state.wxRenderer.getRenderer(state.citeStatus)
}) })
// 去除第一行的 margin-top // 去除第一行的 margin-top
output = output.replace(/(style=".*?)"/, '$1;margin-top: 0"') output = output.replace(/(style=".*?)"/, '$1;margin-top: 0"')
if (state.status) { if (state.citeStatus) {
// 引用脚注 // 引用脚注
output += state.wxRenderer.buildFootnotes() output += state.wxRenderer.buildFootnotes()
// 附加的一些 style // 附加的一些 style