Merge branch 'master' of github.com:doocs/md

This commit is contained in:
yanglbme 2020-10-21 17:48:44 +08:00
commit 5ae6fd4b28
16 changed files with 1401 additions and 970 deletions

View File

@ -6,36 +6,38 @@
</template> </template>
<script> <script>
import Loading from './components/Loading' import Loading from "./components/Loading";
import CodemirrorEditor from './view/CodemirrorEditor' import CodemirrorEditor from "./view/CodemirrorEditor";
export default { export default {
name: 'App', name: "App",
components: { components: {
Loading, Loading,
CodemirrorEditor CodemirrorEditor,
}, },
data() { data() {
return { return {
loading: true loading: true,
} };
}, },
mounted() { mounted() {
setTimeout(() => { setTimeout(() => {
this.loading = false this.loading = false;
}, 100); }, 100);
} },
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.fade-enter, .fade-leave-to { .fade-enter,
.fade-leave-to {
opacity: 0; opacity: 0;
} }
.fade-enter-to, .fade-leave { .fade-enter-to,
.fade-leave {
opacity: 1; opacity: 1;
} }
.fade-enter-active, .fade-leave-active { .fade-enter-active,
.fade-leave-active {
transition: all 1s; transition: all 1s;
} }
</style> </style>

View File

@ -1,4 +1,4 @@
@import './code-theme.less'; @import "./code-theme.less";
* { * {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
@ -27,11 +27,12 @@ em {
html, html,
body { body {
height: 100%; height: 100%;
font-family: 'PingFang SC', BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif; font-family: "PingFang SC", BlinkMacSystemFont, Roboto, "Helvetica Neue",
sans-serif;
} }
.el-message__icon { .el-message__icon {
display: none display: none;
} }
.container { .container {
@ -143,7 +144,8 @@ section {
font-size: 14px; font-size: 14px;
padding: 20px; padding: 20px;
width: 100% !important; width: 100% !important;
font-family: 'PingFang SC', BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif !important; font-family: "PingFang SC", BlinkMacSystemFont, Roboto, "Helvetica Neue",
sans-serif !important;
} }
/* ele ui */ /* ele ui */
@ -158,7 +160,7 @@ section {
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 6px; width: 6px;
height: 6px; height: 6px;
background-color: #FFF; background-color: #fff;
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
@ -169,7 +171,7 @@ section {
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
border-radius: 6px; border-radius: 6px;
background-color: rgba(144, 146, 152, 0.5); background-color: rgba(144, 146, 152, 0.5);
transition: background-color .3s; transition: background-color 0.3s;
} }
::-webkit-scrollbar-thumb:hover { ::-webkit-scrollbar-thumb:hover {

View File

@ -1,2 +1,2 @@
@import './codeTheme/wechat-code-block.less'; @import "./codeTheme/wechat-code-block.less";
@import './codeTheme/github-code-block.less'; @import "./codeTheme/github-code-block.less";

View File

@ -5,7 +5,7 @@
margin: 10px 8px; margin: 10px 8px;
position: relative; position: relative;
height: auto; height: auto;
background-color: rgba(27,31,35,.05); background-color: rgba(27, 31, 35, 0.05);
.code-snippet__line-index { .code-snippet__line-index {
display: none; display: none;

View File

@ -10,7 +10,7 @@
margin: 10px 8px; margin: 10px 8px;
color: #333; color: #333;
position: relative; position: relative;
background-color: rgba(27, 31, 35, .05); background-color: rgba(27, 31, 35, 0.05);
border: 1px solid #f0f0f0; border: 1px solid #f0f0f0;
border-radius: 2px; border-radius: 2px;
display: flex; display: flex;

View File

@ -1,2 +1,72 @@
/*! Color themes for Google Code Prettify | MIT License | github.com/jmblog/color-themes-for-google-code-prettify */ /*! Color themes for Google Code Prettify | MIT License | github.com/jmblog/color-themes-for-google-code-prettify */
.prettyprint{font-family:Menlo,Bitstream Vera Sans Mono,DejaVu Sans Mono,Monaco,Consolas,monospace;border:0!important}.pln{color:#333}ol.linenums{margin-top:0;margin-bottom:0;color:#ccc}li.L0,li.L1,li.L2,li.L3,li.L4,li.L5,li.L6,li.L7,li.L8,li.L9{padding-left:1em;background-color:#fff;list-style-type:decimal}@media screen{.str{color:#183691}.kwd{color:#a71d5d}.com{color:#969896}.typ{color:#0086b3}.lit{color:#0086b3}.pun{color:#333}.opn{color:#333}.clo{color:#333}.tag{color:navy}.atn{color:#795da3}.atv{color:#183691}.dec{color:#333}.var{color:teal}.fun{color:#900}} .prettyprint {
font-family: Menlo, Bitstream Vera Sans Mono, DejaVu Sans Mono, Monaco,
Consolas, monospace;
border: 0 !important;
}
.pln {
color: #333;
}
ol.linenums {
margin-top: 0;
margin-bottom: 0;
color: #ccc;
}
li.L0,
li.L1,
li.L2,
li.L3,
li.L4,
li.L5,
li.L6,
li.L7,
li.L8,
li.L9 {
padding-left: 1em;
background-color: #fff;
list-style-type: decimal;
}
@media screen {
.str {
color: #183691;
}
.kwd {
color: #a71d5d;
}
.com {
color: #969896;
}
.typ {
color: #0086b3;
}
.lit {
color: #0086b3;
}
.pun {
color: #333;
}
.opn {
color: #333;
}
.clo {
color: #333;
}
.tag {
color: navy;
}
.atn {
color: #795da3;
}
.atv {
color: #183691;
}
.dec {
color: #333;
}
.var {
color: teal;
}
.fun {
color: #900;
}
}

View File

@ -21,14 +21,14 @@
} }
.cm-s-style-mirror .CodeMirror-line::selection, .cm-s-style-mirror .CodeMirror-line::selection,
.cm-s-style-mirror .CodeMirror-line>span::selection, .cm-s-style-mirror .CodeMirror-line > span::selection,
.cm-s-style-mirror .CodeMirror-line>span>span::selection { .cm-s-style-mirror .CodeMirror-line > span > span::selection {
background: #e0e0e0; background: #e0e0e0;
} }
.cm-s-style-mirror .CodeMirror-line::-moz-selection, .cm-s-style-mirror .CodeMirror-line::-moz-selection,
.cm-s-style-mirror .CodeMirror-line>span::-moz-selection, .cm-s-style-mirror .CodeMirror-line > span::-moz-selection,
.cm-s-style-mirror .CodeMirror-line>span>span::-moz-selection { .cm-s-style-mirror .CodeMirror-line > span > span::-moz-selection {
background: #e0e0e0; background: #e0e0e0;
} }

View File

@ -30,14 +30,15 @@
box-shadow: inset 0 0 0 1px rgba(233, 231, 231, 0.102); box-shadow: inset 0 0 0 1px rgba(233, 231, 231, 0.102);
} }
.code-snippet__fix { .code-snippet__fix {
background-color: rgb(238,238,238); background-color: rgb(238, 238, 238);
} }
} }
.cm-s-style-mirror .CodeMirror-matchingbracket { .cm-s-style-mirror .CodeMirror-matchingbracket {
color: @nightWhiteColor!important; color: @nightWhiteColor!important;
background: rgb(30, 30, 30)!important; background: rgb(30, 30, 30) !important;
} }
.cm-s-xq-light span.cm-variable-2, .cm-s-style-mirror span.cm-tag { .cm-s-xq-light span.cm-variable-2,
.cm-s-style-mirror span.cm-tag {
color: @nightFontColor; color: @nightFontColor;
} }
.cm-s-xq-light .CodeMirror-activeline-background { .cm-s-xq-light .CodeMirror-activeline-background {
@ -57,7 +58,8 @@
background-color: @nightCodeMirrorColor; background-color: @nightCodeMirrorColor;
border: 1px solid transparent; border: 1px solid transparent;
} }
.el-button.is-plain:focus, .el-button.is-plain:hover { .el-button.is-plain:focus,
.el-button.is-plain:hover {
background: @nightButtonBg; background: @nightButtonBg;
color: @nightWhiteColor; color: @nightWhiteColor;
border: 1px solid @nightWhiteColor; border: 1px solid @nightWhiteColor;
@ -65,14 +67,18 @@
color: @nightWhiteColor; color: @nightWhiteColor;
} }
} }
.insert__dialog, .about__dialog, .reset__dialog, .upload__dialog { .insert__dialog,
.about__dialog,
.reset__dialog,
.upload__dialog {
.el-dialog { .el-dialog {
background-color: @nightBgColor; background-color: @nightBgColor;
} }
.el-dialog__body { .el-dialog__body {
color: @nightWhiteColor; color: @nightWhiteColor;
} }
.el-dialog__title, .el-form-item__label { .el-dialog__title,
.el-form-item__label {
color: @nightWhiteColor; color: @nightWhiteColor;
} }
.el-tabs__item { .el-tabs__item {
@ -85,7 +91,10 @@
background-color: @nightButtonBg; background-color: @nightButtonBg;
} }
} }
::v-deep .el-icon-upload, .el-icon-download, .el-icon-refresh, .el-icon-s-grid { ::v-deep .el-icon-upload,
.el-icon-download,
.el-icon-refresh,
.el-icon-s-grid {
color: @nightWhiteColor; color: @nightWhiteColor;
} }
::-webkit-scrollbar { ::-webkit-scrollbar {

View File

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

View File

@ -1,162 +1,278 @@
<template> <template>
<el-container class="top is-dark"> <el-container class="top is-dark">
<!-- 图片上传 --> <!-- 图片上传 -->
<el-tooltip :effect="effect" content="上传图片" placement="bottom-start"> <el-tooltip
<i class="el-icon-upload" size="medium" @click="$emit('showDialogUploadImg')"></i> :effect="effect"
content="上传图片"
placement="bottom-start"
>
<i
class="el-icon-upload"
size="medium"
@click="$emit('showDialogUploadImg')"
></i>
</el-tooltip> </el-tooltip>
<!-- 下载文本文档 --> <!-- 下载文本文档 -->
<el-tooltip class="header__item" :effect="effect" content="下载编辑框Markdown文档" placement="bottom-start"> <el-tooltip
<i class="el-icon-download" size="medium" @click="$emit('downLoad')"></i> class="header__item"
:effect="effect"
content="下载编辑框Markdown文档"
placement="bottom-start"
>
<i
class="el-icon-download"
size="medium"
@click="$emit('downLoad')"
></i>
</el-tooltip> </el-tooltip>
<!-- 页面重置 --> <!-- 页面重置 -->
<el-tooltip class="header__item" :effect="effect" content="重置页面" placement="bottom-start"> <el-tooltip
<i class="el-icon-refresh" size="medium" @click="showResetConfirm = true"></i> class="header__item"
:effect="effect"
content="重置页面"
placement="bottom-start"
>
<i
class="el-icon-refresh"
size="medium"
@click="showResetConfirm = true"
></i>
</el-tooltip> </el-tooltip>
<!-- 插入表格 --> <!-- 插入表格 -->
<el-tooltip class="header__item header__item_last" :effect="effect" content="插入表格" placement="bottom-start"> <el-tooltip
<i class="el-icon-s-grid" size="medium" @click="$emit('showDialogForm')"></i> class="header__item header__item_last"
:effect="effect"
content="插入表格"
placement="bottom-start"
>
<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="selectFont" size="mini" placeholder="选择字体" clearable @change="fontChanged"> <el-select
<el-option v-for="font in config.builtinFonts" :style="{fontFamily: font.value}" :key="font.value" v-model="selectFont"
:label="font.label" :value="font.value"> size="mini"
placeholder="选择字体"
clearable
@change="fontChanged"
>
<el-option
v-for="font in config.builtinFonts"
:style="{ fontFamily: font.value }"
:key="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>
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-select v-model="selectSize" size="mini" placeholder="选择段落字号" clearable @change="sizeChanged"> <el-select
<el-option v-for="size in config.sizeOption" :key="size.value" :label="size.label" :value="size.value"> v-model="selectSize"
size="mini"
placeholder="选择段落字号"
clearable
@change="sizeChanged"
>
<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="selectColor" size="mini" placeholder="选择颜色" clearable @change="colorChanged"> <el-select
<el-option v-for="color in config.colorOption" :key="color.value" :label="color.label" :value="color.value"> 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-left">{{ color.label }}</span>
<span class="select-item-right">{{ color.desc }}</span> <span class="select-item-right">{{ color.desc }}</span>
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-tooltip content="自定义颜色" :effect="effect" placement="top"> <el-tooltip content="自定义颜色" :effect="effect" placement="top">
<el-color-picker v-model="selectColor" 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>
<el-tooltip content="微信外链自动转为文末引用" :effect="effect" placement="top"> <el-tooltip
<el-switch class="header__switch" v-model="citeStatus" active-color="#67c23a" inactive-color="#dcdfe6" @change="statusChanged"> content="微信外链自动转为文末引用"
:effect="effect"
placement="top"
>
<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>
<el-tooltip class="item" :effect="effect" content="自定义CSS样式" placement="left"> <el-tooltip
<el-button :type="btnType" plain size="medium" icon="el-icon-setting" @click="customStyle"></el-button> class="item"
:effect="effect"
content="自定义CSS样式"
placement="left"
>
<el-button
:type="btnType"
plain
size="medium"
icon="el-icon-setting"
@click="customStyle"
></el-button>
</el-tooltip> </el-tooltip>
<el-button :type="btnType" plain size="medium" @click="copy" placement="bottom-start">复制</el-button> <el-button
<el-button :type="btnType" plain size="medium" class="about" @click="$emit('showAboutDialog')">关于</el-button> :type="btnType"
<el-tooltip :content="btnContent" :effect="effect" placement="bottom-start"> plain
<div class="mode__switch mode__switch_black" v-if="nightMode" @click="themeChanged"></div> size="medium"
@click="copy"
placement="bottom-start"
>复制</el-button
>
<el-button
:type="btnType"
plain
size="medium"
class="about"
@click="$emit('showAboutDialog')"
>关于</el-button
>
<el-tooltip
:content="btnContent"
:effect="effect"
placement="bottom-start"
>
<div
class="mode__switch mode__switch_black"
v-if="nightMode"
@click="themeChanged"
></div>
<div class="mode__switch" v-else @click="themeChanged"></div> <div class="mode__switch" v-else @click="themeChanged"></div>
</el-tooltip> </el-tooltip>
<resetDialog :showResetConfirm="showResetConfirm" @confirm="confirmReset" @close="cancelReset"/> <resetDialog
:showResetConfirm="showResetConfirm"
@confirm="confirmReset"
@close="cancelReset"
/>
</el-container> </el-container>
</template> </template>
<script> <script>
import { import {
downLoadMD, downLoadMD,
setFontSize, setFontSize,
fixCodeWhiteSpace, fixCodeWhiteSpace,
setColorWithCustomTemplate setColorWithCustomTemplate,
} from '../../assets/scripts/util' } from "../../assets/scripts/util";
import { import { solveWeChatImage, solveHtml } from "../../assets/scripts/converter";
solveWeChatImage, import config from "../../assets/scripts/config";
solveHtml import DEFAULT_CSS_CONTENT from "../../assets/scripts/themes/default-theme-css";
} from '../../assets/scripts/converter' import resetDialog from "./resetDialog";
import config from '../../assets/scripts/config' import { mapState, mapMutations } from "vuex";
import DEFAULT_CSS_CONTENT from '../../assets/scripts/themes/default-theme-css'
import resetDialog from './resetDialog'
import {mapState, mapMutations} from 'vuex'
export default { export default {
name: 'editor-header', name: "editor-header",
data() { data() {
return { return {
config: config, config: config,
citeStatus: false, citeStatus: false,
showResetConfirm: false, showResetConfirm: false,
selectFont: '', selectFont: "",
selectSize: '', selectSize: "",
selectColor: '', selectColor: "",
selectCodeTheme: 'github' selectCodeTheme: "github",
}; };
}, },
components: { components: {
resetDialog resetDialog,
}, },
computed: { computed: {
effect() { effect() {
return this.nightMode ? 'dark' : 'light' return this.nightMode ? "dark" : "light";
}, },
btnContent() { btnContent() {
return this.nightMode ? '浅色模式' : '暗黑模式' return this.nightMode ? "浅色模式" : "暗黑模式";
}, },
btnType() { btnType() {
return this.nightMode ? 'default' : 'primary'; return this.nightMode ? "default" : "primary";
}, },
...mapState({ ...mapState({
output: state => state.output, output: (state) => state.output,
editor: state => state.editor, editor: (state) => state.editor,
cssEditor: state => state.cssEditor, cssEditor: (state) => state.cssEditor,
currentFont: state => state.currentFont, currentFont: (state) => state.currentFont,
currentSize: state => state.currentSize, currentSize: (state) => state.currentSize,
currentColor: state => state.currentColor, currentColor: (state) => state.currentColor,
codeTheme: state => state.codeTheme, codeTheme: (state) => state.codeTheme,
nightMode: state => state.nightMode nightMode: (state) => state.nightMode,
}) }),
}, },
methods: { methods: {
fontChanged(fonts) { fontChanged(fonts) {
this.setWxRendererOptions({ this.setWxRendererOptions({
fonts: fonts fonts: fonts,
}); });
this.setCurrentFont(fonts); this.setCurrentFont(fonts);
this.$emit('refresh'); this.$emit("refresh");
}, },
sizeChanged(size) { sizeChanged(size) {
let theme = setFontSize(size.replace('px', '')) let theme = setFontSize(size.replace("px", ""));
theme = setColorWithCustomTemplate(theme, this.currentColor) theme = setColorWithCustomTemplate(theme, this.currentColor);
this.setWxRendererOptions({ this.setWxRendererOptions({
size: size, size: size,
theme: theme theme: theme,
}); });
this.setCurrentSize(size); this.setCurrentSize(size);
this.$emit('refresh'); this.$emit("refresh");
}, },
colorChanged(color) { colorChanged(color) {
let theme = setFontSize(this.currentSize.replace('px', '')); let theme = setFontSize(this.currentSize.replace("px", ""));
theme = setColorWithCustomTemplate(theme, color); theme = setColorWithCustomTemplate(theme, color);
this.setWxRendererOptions({ this.setWxRendererOptions({
theme: theme theme: theme,
}); });
this.setCurrentColor(color); this.setCurrentColor(color);
this.$emit('refresh'); this.$emit("refresh");
}, },
codeThemeChanged(theme) { codeThemeChanged(theme) {
this.setCurrentCodeTheme(theme); this.setCurrentCodeTheme(theme);
this.$emit('refresh'); this.$emit("refresh");
}, },
statusChanged(val) { statusChanged(val) {
this.setCiteStatus(val); this.setCiteStatus(val);
this.$emit('refresh'); this.$emit("refresh");
}, },
// //
copy(e) { copy(e) {
this.$emit('startCopy'); this.$emit("startCopy");
setTimeout(() => { setTimeout(() => {
let clipboardDiv = document.getElementById('output'); let clipboardDiv = document.getElementById("output");
solveWeChatImage(); solveWeChatImage();
fixCodeWhiteSpace(); fixCodeWhiteSpace();
solveHtml(); solveHtml();
@ -167,36 +283,37 @@ export default {
range.setStartBefore(clipboardDiv.firstChild); range.setStartBefore(clipboardDiv.firstChild);
range.setEndAfter(clipboardDiv.lastChild); range.setEndAfter(clipboardDiv.lastChild);
window.getSelection().addRange(range); window.getSelection().addRange(range);
document.execCommand('copy'); document.execCommand("copy");
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges();
fixCodeWhiteSpace('normal'); fixCodeWhiteSpace("normal");
clipboardDiv.innerHTML = this.output; clipboardDiv.innerHTML = this.output;
// //
this.$notify({ this.$notify({
showClose: true, showClose: true,
message: '已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴', message:
"已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴",
offset: 80, offset: 80,
duration: 1600, duration: 1600,
type: 'success' type: "success",
}); });
this.$emit('refresh'); this.$emit("refresh");
this.$emit('endCopy'); this.$emit("endCopy");
}, 350); }, 350);
e.target.blur(); e.target.blur();
}, },
// CSS // CSS
async customStyle() { async customStyle() {
this.$emit('showCssEditor'); this.$emit("showCssEditor");
this.$nextTick(() => { this.$nextTick(() => {
if(!this.cssEditor) { if (!this.cssEditor) {
this.cssEditor.refresh(); this.cssEditor.refresh();
} }
}) });
setTimeout(() => { setTimeout(() => {
this.cssEditor.refresh(); this.cssEditor.refresh();
},50) }, 50);
let flag = await localStorage.getItem('__css_content'); let flag = await localStorage.getItem("__css_content");
if (!flag) { if (!flag) {
this.setCssEditorValue(DEFAULT_CSS_CONTENT); this.setCssEditorValue(DEFAULT_CSS_CONTENT);
@ -204,15 +321,15 @@ export default {
}, },
// //
confirmReset() { confirmReset() {
localStorage.clear() localStorage.clear();
this.clearEditorToDefault(); this.clearEditorToDefault();
this.editor.focus() this.editor.focus();
this.citeStatus = false; this.citeStatus = false;
this.statusChanged(false); this.statusChanged(false);
this.fontChanged(this.config.builtinFonts[0].value) this.fontChanged(this.config.builtinFonts[0].value);
this.colorChanged(this.config.colorOption[1].value) this.colorChanged(this.config.colorOption[1].value);
this.sizeChanged(this.config.sizeOption[2].value) this.sizeChanged(this.config.sizeOption[2].value);
this.$emit('cssChanged') this.$emit("cssChanged");
this.selectFont = this.currentFont; this.selectFont = this.currentFont;
this.selectSize = this.currentSize; this.selectSize = this.currentSize;
this.selectColor = this.currentColor; this.selectColor = this.currentColor;
@ -220,27 +337,27 @@ export default {
}, },
cancelReset() { cancelReset() {
this.showResetConfirm = false; this.showResetConfirm = false;
this.editor.focus() this.editor.focus();
}, },
...mapMutations([ ...mapMutations([
'clearEditorToDefault', "clearEditorToDefault",
'setCurrentColor', "setCurrentColor",
'setCiteStatus', "setCiteStatus",
'themeChanged', "themeChanged",
'setCurrentFont', "setCurrentFont",
'setCurrentSize', "setCurrentSize",
'setCssEditorValue', "setCssEditorValue",
'setCurrentCodeTheme', "setCurrentCodeTheme",
'setWxRendererOptions' "setWxRendererOptions",
]) ]),
}, },
mounted() { mounted() {
this.selectFont = this.currentFont; this.selectFont = this.currentFont;
this.selectSize = this.currentSize; this.selectSize = this.currentSize;
this.selectColor = this.currentColor; this.selectColor = this.currentColor;
this.selectCodeTheme = this.codeTheme; this.selectCodeTheme = this.codeTheme;
} },
} };
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -260,12 +377,12 @@ export default {
margin-left: 24px; margin-left: 24px;
width: 24px; width: 24px;
height: 24px; height: 24px;
background: url('../../assets/images/night.png') no-repeat; background: url("../../assets/images/night.png") no-repeat;
background-size: cover; background-size: cover;
transition: all .3s; transition: all 0.3s;
} }
.mode__switch_black { .mode__switch_black {
background: url('../../assets/images/light.png') no-repeat; background: url("../../assets/images/light.png") no-repeat;
background-size: cover; background-size: cover;
} }
.top { .top {

View File

@ -29,43 +29,48 @@
</el-col> </el-col>
</el-row> </el-row>
<table style="border-collapse: collapse" class="input-table"> <table style="border-collapse: collapse" class="input-table">
<tr :class="{ 'head-style': row === 1 }" v-for="row in rowNum+1" :key="row"> <tr
:class="{ 'head-style': row === 1 }"
v-for="row in rowNum + 1"
:key="row"
>
<td v-for="col in colNum" :key="col"> <td v-for="col in colNum" :key="col">
<el-input <el-input
align="center" align="center"
v-model="tableData[`k_${row-1}_${col-1}`]" v-model="tableData[`k_${row - 1}_${col - 1}`]"
:placeholder="row===1?'表头':''" :placeholder="row === 1 ? '表头' : ''"
/> />
</td> </td>
</tr> </tr>
</table> </table>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button :type="btnType" plain @click="$emit('input', false)"> </el-button> <el-button :type="btnType" plain @click="$emit('input', false)"
<el-button :type="btnType" @click="insertTable" plain> </el-button> > </el-button
>
<el-button :type="btnType" @click="insertTable" plain
> </el-button
>
</div> </div>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import config from "../../assets/scripts/config"; import config from "../../assets/scripts/config";
import {createTable} from '../../assets/scripts/util'; import { createTable } from "../../assets/scripts/util";
import { import { mapState, mapMutations } from "vuex";
mapState,
mapMutations
} from "vuex";
export default { export default {
props: { props: {
value: { value: {
type: Boolean, type: Boolean,
default: false default: false,
} },
}, },
data() { data() {
return { return {
config: config, config: config,
rowNum: 3, rowNum: 3,
colNum: 3, colNum: 3,
tableData: {} tableData: {},
}; };
}, },
computed: { computed: {
@ -73,9 +78,9 @@ export default {
return this.nightMode ? "default" : "primary"; return this.nightMode ? "default" : "primary";
}, },
...mapState({ ...mapState({
nightMode: state => state.nightMode, nightMode: (state) => state.nightMode,
editor: state => state.editor editor: (state) => state.editor,
}) }),
}, },
methods: { methods: {
// //
@ -84,18 +89,18 @@ export default {
const table = createTable({ const table = createTable({
data: this.tableData, data: this.tableData,
rows: this.rowNum, rows: this.rowNum,
cols: this.colNum cols: this.colNum,
}); });
this.tableData = {}; this.tableData = {};
this.rowNum = 3; this.rowNum = 3;
this.colNum = 3; this.colNum = 3;
this.editor.replaceSelection(`\n${table}\n`, "end"); this.editor.replaceSelection(`\n${table}\n`, "end");
this.$emit('input', false); this.$emit("input", false);
this.editorRefresh(); this.editorRefresh();
}, },
...mapMutations(["editorRefresh"]) ...mapMutations(["editorRefresh"]),
} },
}; };
</script> </script>

View File

@ -1,49 +1,54 @@
<template> <template>
<el-dialog title="提示" class="reset__dialog" :visible="showResetConfirm" @close="$emit('close')"> <el-dialog
title="提示"
class="reset__dialog"
:visible="showResetConfirm"
@close="$emit('close')"
>
<div class="text"> <div class="text">
此操作将丢失本地缓存的文本和自定义样式是否继续? 此操作将丢失本地缓存的文本和自定义样式是否继续?
</div> </div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button :type="btnType" plain @click="$emit('close')"> </el-button> <el-button :type="btnType" plain @click="$emit('close')"
<el-button :type="btnType" @click="$emit('confirm')" plain> </el-button> > </el-button
>
<el-button :type="btnType" @click="$emit('confirm')" plain
> </el-button
>
</div> </div>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import { import { mapState } from "vuex";
mapState export default {
} from 'vuex';
export default {
props: { props: {
showResetConfirm: { showResetConfirm: {
type: Boolean, type: Boolean,
default: false default: false,
} },
}, },
computed: { computed: {
btnType() { btnType() {
return this.nightMode ? 'default' : 'primary'; return this.nightMode ? "default" : "primary";
}, },
...mapState({ ...mapState({
nightMode: state => state.nightMode nightMode: (state) => state.nightMode,
}) }),
} },
} };
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.reset__dialog { .reset__dialog {
text-align: center; text-align: center;
} }
.text { .text {
text-align: center; text-align: center;
} }
.dialog-footer { .dialog-footer {
text-align: center; text-align: center;
} }
</style> </style>

View File

@ -1,62 +1,70 @@
<template> <template>
<ul v-show="value" id="menu" class="menu" :style="`left: ${left}px;top: ${top}px;`"> <ul
<li v-for="item of list" :key="item.key" class="menu_item" @mousedown="onMouseDown(item.key)"> v-show="value"
<span>{{item.text}}</span> id="menu"
class="menu"
:style="`left: ${left}px;top: ${top}px;`"
>
<li
v-for="item of list"
:key="item.key"
class="menu_item"
@mousedown="onMouseDown(item.key)"
>
<span>{{ item.text }}</span>
</li> </li>
</ul> </ul>
</template> </template>
<script> <script>
import { import { uploadImgFile } from "../../assets/scripts/uploadImageFile";
uploadImgFile,
} from '../../assets/scripts/uploadImageFile';
export default { export default {
props: { props: {
value: { value: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
top: { top: {
type: Number, type: Number,
default: 0 default: 0,
}, },
left: { left: {
type: Number, type: Number,
default: 0 default: 0,
} },
}, },
data() { data() {
return { return {
list: [ list: [
{ {
text: '上传图片', text: "上传图片",
key: 'insertPic' key: "insertPic",
}, },
{ {
text: '插入表格', text: "插入表格",
key: 'insertTable' key: "insertTable",
}, },
{ {
text: '页面重置', text: "页面重置",
key: 'pageReset' key: "pageReset",
}, },
{ {
text: '下载MD文档', text: "下载MD文档",
key: 'downLoad' key: "downLoad",
} },
] ],
} };
}, },
methods: { methods: {
closeCB() { closeCB() {
this.$emit('input', false); this.$emit("input", false);
}, },
onMouseDown(key){ onMouseDown(key) {
this.$emit('menuTick', key) this.$emit("menuTick", key);
this.$emit('closeMenu', false) this.$emit("closeMenu", false);
}
}, },
} },
};
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -83,7 +91,8 @@ export default {
color: white; color: white;
background: rgb(139, 146, 148); background: rgb(139, 146, 148);
} }
span,.btn-upload { span,
.btn-upload {
text-align: center; text-align: center;
display: inline-block; display: inline-block;
padding: 4px 0; padding: 4px 0;
@ -91,7 +100,7 @@ export default {
} }
.btn-upload { .btn-upload {
margin: 0; margin: 0;
border:none; border: none;
outline: none; outline: none;
background: transparent; background: transparent;
} }
@ -104,7 +113,6 @@ export default {
} }
} }
li:hover { li:hover {
background-color: #1790ff; background-color: #1790ff;
color: white; color: white;
@ -114,5 +122,4 @@ li {
font-size: 15px; font-size: 15px;
list-style: none; list-style: none;
} }
</style> </style>

View File

@ -1,14 +1,37 @@
<template> <template>
<el-dialog title="本地上传" class="upload__dialog" :visible="value" @close="$emit('close')"> <el-dialog
title="本地上传"
class="upload__dialog"
:visible="value"
@close="$emit('close')"
>
<el-tabs type="card" :value="'upload'"> <el-tabs type="card" :value="'upload'">
<el-tab-pane class="upload-panel" label="选择上传" name="upload"> <el-tab-pane class="upload-panel" label="选择上传" name="upload">
<el-select v-model="imgHost" @change="changeImgHost" placeholder="请选择" size="small"> <el-select
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> v-model="imgHost"
@change="changeImgHost"
placeholder="请选择"
size="small"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option> </el-option>
</el-select> </el-select>
<el-upload drag action :headers="{'Content-Type': 'multipart/form-data'}" :show-file-list="false" <el-upload
:multiple="true" accept=".jpg, .jpeg, .png, .gif" name="file" :before-upload="beforeUpload" drag
v-loading="uploadingImg"> action
:headers="{ 'Content-Type': 'multipart/form-data' }"
:show-file-list="false"
:multiple="true"
accept=".jpg, .jpeg, .png, .gif"
name="file"
:before-upload="beforeUpload"
v-loading="uploadingImg"
>
<i class="el-icon-upload"></i> <i class="el-icon-upload"></i>
<div class="el-upload__text"> <div class="el-upload__text">
将图片拖到此处 将图片拖到此处
@ -17,88 +40,164 @@
</el-upload> </el-upload>
</el-tab-pane> </el-tab-pane>
<el-tab-pane class="github-panel" label="GitHub 图床" name="github"> <el-tab-pane class="github-panel" label="GitHub 图床" name="github">
<el-form class="setting-form" ref="form" :model="formGitHub" label-position="right" label-width="140px"> <el-form
class="setting-form"
ref="form"
:model="formGitHub"
label-position="right"
label-width="140px"
>
<el-form-item label="GitHub 仓库" :required="true"> <el-form-item label="GitHub 仓库" :required="true">
<el-input v-model.trim="formGitHub.repo" placeholder="如github.com/yanglbme/resource"></el-input> <el-input
v-model.trim="formGitHub.repo"
placeholder="如github.com/yanglbme/resource"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="分支"> <el-form-item label="分支">
<el-input v-model.trim="formGitHub.branch" placeholder="如release可不填默认 master"></el-input> <el-input
v-model.trim="formGitHub.branch"
placeholder="如release可不填默认 master"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="Token" :required="true"> <el-form-item label="Token" :required="true">
<el-input v-model.trim="formGitHub.accessToken" show-password <el-input
placeholder="如cc1d0c1426d0fd0902bd2d7184b14da61b8abc46"></el-input> v-model.trim="formGitHub.accessToken"
<el-link type="primary" show-password
placeholder="如cc1d0c1426d0fd0902bd2d7184b14da61b8abc46"
></el-input>
<el-link
type="primary"
href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token" href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token"
target="_blank">如何获取 GitHub Token</el-link> target="_blank"
>如何获取 GitHub Token</el-link
>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="saveGitHubConfiguration">保存配置</el-button> <el-button
type="primary"
@click="saveGitHubConfiguration"
>保存配置</el-button
>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-tab-pane> </el-tab-pane>
<el-tab-pane class="github-panel" label="阿里云 OSS" name="aliOSS"> <el-tab-pane class="github-panel" label="阿里云 OSS" name="aliOSS">
<el-form class="setting-form" ref="form" :model="formAliOSS" label-position="right" label-width="140px"> <el-form
class="setting-form"
ref="form"
:model="formAliOSS"
label-position="right"
label-width="140px"
>
<el-form-item label="AccessKey ID" :required="true"> <el-form-item label="AccessKey ID" :required="true">
<el-input v-model.trim="formAliOSS.accessKeyId" placeholder="如LTAI4GdoocsmdoxUf13ylbaNHk"></el-input> <el-input
v-model.trim="formAliOSS.accessKeyId"
placeholder="如LTAI4GdoocsmdoxUf13ylbaNHk"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="AccessKey Secret" :required="true"> <el-form-item label="AccessKey Secret" :required="true">
<el-input v-model.trim="formAliOSS.accessKeySecret" show-password <el-input
placeholder="如cc1d0c142doocs0902bd2d7md4b14da6ylbabc46"></el-input> v-model.trim="formAliOSS.accessKeySecret"
show-password
placeholder="如cc1d0c142doocs0902bd2d7md4b14da6ylbabc46"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="Bucket" :required="true"> <el-form-item label="Bucket" :required="true">
<el-input v-model.trim="formAliOSS.bucket" <el-input
placeholder="如doocs"></el-input> v-model.trim="formAliOSS.bucket"
placeholder="如doocs"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="Bucket 所在区域" :required="true"> <el-form-item label="Bucket 所在区域" :required="true">
<el-input v-model.trim="formAliOSS.region" <el-input
placeholder="如oss-cn-shenzhen"></el-input> v-model.trim="formAliOSS.region"
placeholder="如oss-cn-shenzhen"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="自定义 CDN 域名" :required="false"> <el-form-item label="自定义 CDN 域名" :required="false">
<el-input v-model.trim="formAliOSS.cdnHost" <el-input
placeholder="如https://imagecdn.alidaodao.com可不填"></el-input> v-model.trim="formAliOSS.cdnHost"
placeholder="如https://imagecdn.alidaodao.com可不填"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="存储路径"> <el-form-item label="存储路径">
<el-input v-model.trim="formAliOSS.path" <el-input
placeholder="如img可不填默认为根目录"></el-input> v-model.trim="formAliOSS.path"
<el-link type="primary" placeholder="如img可不填默认为根目录"
></el-input>
<el-link
type="primary"
href="https://help.aliyun.com/document_detail/31883.html" href="https://help.aliyun.com/document_detail/31883.html"
target="_blank">如何使用阿里云 OSS</el-link> target="_blank"
>如何使用阿里云 OSS</el-link
>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="saveAliOSSConfiguration">保存配置</el-button> <el-button
type="primary"
@click="saveAliOSSConfiguration"
>保存配置</el-button
>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-tab-pane> </el-tab-pane>
<el-tab-pane class="github-panel" label="腾讯云 COS" name="txCOS"> <el-tab-pane class="github-panel" label="腾讯云 COS" name="txCOS">
<el-form class="setting-form" ref="form" :model="formTxCOS" label-position="right" label-width="140px"> <el-form
class="setting-form"
ref="form"
:model="formTxCOS"
label-position="right"
label-width="140px"
>
<el-form-item label="SecretId" :required="true"> <el-form-item label="SecretId" :required="true">
<el-input v-model.trim="formTxCOS.secretId" placeholder="如AKIDnQp1w3DOOCSs8F5MDp9tdoocsmdUPonW3"></el-input> <el-input
v-model.trim="formTxCOS.secretId"
placeholder="如AKIDnQp1w3DOOCSs8F5MDp9tdoocsmdUPonW3"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="SecretKey" :required="true"> <el-form-item label="SecretKey" :required="true">
<el-input v-model.trim="formTxCOS.secretKey" show-password <el-input
placeholder="如ukLmdtEJ9271f3DOocsMDsCXdS3YlbW0"></el-input> v-model.trim="formTxCOS.secretKey"
show-password
placeholder="如ukLmdtEJ9271f3DOocsMDsCXdS3YlbW0"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="Bucket" :required="true"> <el-form-item label="Bucket" :required="true">
<el-input v-model.trim="formTxCOS.bucket" <el-input
placeholder="如doocs-3212520134"></el-input> v-model.trim="formTxCOS.bucket"
placeholder="如doocs-3212520134"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="Bucket 所在区域" :required="true"> <el-form-item label="Bucket 所在区域" :required="true">
<el-input v-model.trim="formTxCOS.region" <el-input
placeholder="如ap-guangzhou"></el-input> v-model.trim="formTxCOS.region"
placeholder="如ap-guangzhou"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="自定义 CDN 域名" :required="false"> <el-form-item label="自定义 CDN 域名" :required="false">
<el-input v-model.trim="formTxCOS.cdnHost" <el-input
placeholder="如https://imagecdn.alidaodao.com可不填"></el-input> v-model.trim="formTxCOS.cdnHost"
placeholder="如https://imagecdn.alidaodao.com可不填"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="存储路径"> <el-form-item label="存储路径">
<el-input v-model.trim="formTxCOS.path" <el-input
placeholder="如img可不填默认根目录"></el-input> v-model.trim="formTxCOS.path"
<el-link type="primary" placeholder="如img可不填默认根目录"
></el-input>
<el-link
type="primary"
href="https://cloud.tencent.com/document/product/436/38484" href="https://cloud.tencent.com/document/product/436/38484"
target="_blank">如何使用腾讯云 COS</el-link> target="_blank"
>如何使用腾讯云 COS</el-link
>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="saveTxCOSConfiguration">保存配置</el-button> <el-button
type="primary"
@click="saveTxCOSConfiguration"
>保存配置</el-button
>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-tab-pane> </el-tab-pane>
@ -107,9 +206,7 @@
</template> </template>
<script> <script>
import { import { uploadImgFile } from "../../assets/scripts/uploadImageFile";
uploadImgFile
} from "../../assets/scripts/uploadImageFile";
export default { export default {
props: { props: {
@ -141,7 +238,8 @@ export default {
path: "", path: "",
cdnHost: "", cdnHost: "",
}, },
options: [{ options: [
{
value: "default", value: "default",
label: "默认图床", label: "默认图床",
}, },
@ -151,12 +249,12 @@ export default {
}, },
{ {
value: "aliOSS", value: "aliOSS",
label: "阿里云" label: "阿里云",
}, },
{ {
value: "txCOS", value: "txCOS",
label: "腾讯云" label: "腾讯云",
} },
], ],
imgHost: "default", imgHost: "default",
uploadingImg: false, uploadingImg: false,
@ -181,13 +279,15 @@ export default {
localStorage.setItem("imgHost", this.imgHost); localStorage.setItem("imgHost", this.imgHost);
this.$message({ this.$message({
showClose: true, showClose: true,
message: '已成功切换图床', message: "已成功切换图床",
type: "success", type: "success",
}); });
}, },
saveGitHubConfiguration() { saveGitHubConfiguration() {
if (!(this.formGitHub.repo && this.formGitHub.accessToken)) { if (!(this.formGitHub.repo && this.formGitHub.accessToken)) {
const blankElement = this.formGitHub.repo ? "token" : "GitHub 仓库" const blankElement = this.formGitHub.repo
? "token"
: "GitHub 仓库";
this.$message({ this.$message({
showClose: true, showClose: true,
message: `参数「​${blankElement}」不能为空`, message: `参数「​${blankElement}」不能为空`,
@ -195,14 +295,24 @@ export default {
}); });
return; return;
} }
localStorage.setItem("githubConfig", JSON.stringify(this.formGitHub)); localStorage.setItem(
"githubConfig",
JSON.stringify(this.formGitHub)
);
this.$message({ this.$message({
message: "保存成功", message: "保存成功",
type: "success", type: "success",
}); });
}, },
saveAliOSSConfiguration() { saveAliOSSConfiguration() {
if (!(this.formAliOSS.accessKeyId && this.formAliOSS.accessKeySecret && this.formAliOSS.bucket && this.formAliOSS.region)) { if (
!(
this.formAliOSS.accessKeyId &&
this.formAliOSS.accessKeySecret &&
this.formAliOSS.bucket &&
this.formAliOSS.region
)
) {
this.$message({ this.$message({
showClose: true, showClose: true,
message: `阿里云 OSS 参数配置不全`, message: `阿里云 OSS 参数配置不全`,
@ -210,7 +320,10 @@ export default {
}); });
return; return;
} }
localStorage.setItem("aliOSSConfig", JSON.stringify(this.formAliOSS)); localStorage.setItem(
"aliOSSConfig",
JSON.stringify(this.formAliOSS)
);
this.$message({ this.$message({
message: "保存成功", message: "保存成功",
type: "success", type: "success",
@ -218,7 +331,14 @@ export default {
}, },
saveTxCOSConfiguration() { saveTxCOSConfiguration() {
if (!(this.formTxCOS.secretId && this.formTxCOS.secretKey && this.formTxCOS.bucket && this.formTxCOS.region)) { if (
!(
this.formTxCOS.secretId &&
this.formTxCOS.secretKey &&
this.formTxCOS.bucket &&
this.formTxCOS.region
)
) {
this.$message({ this.$message({
showClose: true, showClose: true,
message: `腾讯云 COS 参数配置不全`, message: `腾讯云 COS 参数配置不全`,
@ -241,11 +361,11 @@ export default {
this.uploadingImg = true; this.uploadingImg = true;
uploadImgFile(file) uploadImgFile(file)
.then(res => { .then((res) => {
this.$emit("uploaded", res); this.$emit("uploaded", res);
this.uploadingImg = false; this.uploadingImg = false;
}) })
.catch(err => { .catch((err) => {
this.uploadingImg = false; this.uploadingImg = false;
this.$message({ this.$message({
showClose: true, showClose: true,
@ -256,20 +376,30 @@ export default {
return false; return false;
}, },
validateConfig() { validateConfig() {
let checkRes = true, errMessage = ''; let checkRes = true,
errMessage = "";
switch (localStorage.getItem('imgHost')) { switch (localStorage.getItem("imgHost")) {
case 'github': case "github":
checkRes = this.formGitHub.repo && this.formGitHub.accessToken; checkRes =
errMessage = checkRes ? '' : '请先配置 GitHub 图床参数'; this.formGitHub.repo && this.formGitHub.accessToken;
errMessage = checkRes ? "" : "请先配置 GitHub 图床参数";
break; break;
case 'aliOSS': case "aliOSS":
checkRes = this.formAliOSS.accessKeyId && this.formAliOSS.accessKeySecret && this.formAliOSS.bucket && this.formAliOSS.region; checkRes =
errMessage = checkRes ? '' : '请先配置阿里云 OSS 参数'; this.formAliOSS.accessKeyId &&
this.formAliOSS.accessKeySecret &&
this.formAliOSS.bucket &&
this.formAliOSS.region;
errMessage = checkRes ? "" : "请先配置阿里云 OSS 参数";
break; break;
case 'txCOS': case "txCOS":
checkRes = this.formTxCOS.secretId && this.formTxCOS.secretKey && this.formTxCOS.bucket && this.formTxCOS.region; checkRes =
errMessage = checkRes ? '' : '请先配置腾讯云 COS 参数'; this.formTxCOS.secretId &&
this.formTxCOS.secretKey &&
this.formTxCOS.bucket &&
this.formTxCOS.region;
errMessage = checkRes ? "" : "请先配置腾讯云 COS 参数";
break; break;
} }
errMessage && this.$message.error(errMessage); errMessage && this.$message.error(errMessage);
@ -319,5 +449,4 @@ export default {
text-align: right; text-align: right;
} }
} }
</style> </style>

View File

@ -7,8 +7,7 @@
</div> </div>
</template> </template>
<script> <script></script>
</script>
<style lang="less" scoped> <style lang="less" scoped>
.loading-wrapper { .loading-wrapper {
@ -42,7 +41,7 @@
display: inline-block; display: inline-block;
width: 100px; width: 100px;
height: 100px; height: 100px;
background: url('../assets/images/favicon.png') no-repeat; background: url("../assets/images/favicon.png") no-repeat;
background-size: cover; background-size: cover;
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="container" :class="{'container_night': nightMode}"> <div class="container" :class="{ container_night: nightMode }">
<el-container> <el-container>
<el-header class="editor__header"> <el-header class="editor__header">
<editor-header <editor-header
@ -11,40 +11,72 @@
@showAboutDialog="aboutDialogVisible = true" @showAboutDialog="aboutDialogVisible = true"
@showDialogForm="dialogFormVisible = true" @showDialogForm="dialogFormVisible = true"
@showDialogUploadImg="dialogUploadImgVisible = true" @showDialogUploadImg="dialogUploadImgVisible = true"
@startCopy="isCoping = true, backLight = true" @startCopy="(isCoping = true), (backLight = true)"
@endCopy="endCopy" @endCopy="endCopy"
/> />
</el-header> </el-header>
<el-main class="main-body"> <el-main class="main-body">
<el-row class="main-section"> <el-row class="main-section">
<el-col :span="12" @contextmenu.prevent.native="openMenu($event)"> <el-col
<textarea id="editor" type="textarea" placeholder="Your markdown text here." v-model="source"> :span="12"
@contextmenu.prevent.native="openMenu($event)"
>
<textarea
id="editor"
type="textarea"
placeholder="Your markdown text here."
v-model="source"
>
</textarea> </textarea>
</el-col> </el-col>
<el-col :span="12" class="preview-wrapper" id="preview" ref="preview" :class="{'preview-wrapper_night': nightMode && isCoping}"> <el-col
<section id="output-wrapper" :class="{'output_night': nightMode && !backLight}"> :span="12"
class="preview-wrapper"
id="preview"
ref="preview"
:class="{
'preview-wrapper_night': nightMode && isCoping,
}"
>
<section
id="output-wrapper"
:class="{ output_night: nightMode && !backLight }"
>
<div class="preview"> <div class="preview">
<section id="output" v-html="output"> <section id="output" v-html="output"></section>
</section> <div
<div class="loading-mask" v-if="nightMode && isCoping"> class="loading-mask"
v-if="nightMode && isCoping"
>
<div class="loading__img"></div> <div class="loading__img"></div>
<span>正在生成</span> <span>正在生成</span>
</div> </div>
</div> </div>
</section> </section>
</el-col> </el-col>
<transition name="custom-classes-transition" enter-active-class="bounceInRight"> <transition
name="custom-classes-transition"
enter-active-class="bounceInRight"
>
<el-col id="cssBox" :span="12" v-show="showCssEditor"> <el-col id="cssBox" :span="12" v-show="showCssEditor">
<textarea id="cssEditor" type="textarea" placeholder="Your custom css here."> <textarea
id="cssEditor"
type="textarea"
placeholder="Your custom css here."
>
</textarea> </textarea>
</el-col> </el-col>
</transition> </transition>
</el-row> </el-row>
</el-main> </el-main>
</el-container> </el-container>
<upload-img-dialog v-model="dialogUploadImgVisible" @close="dialogUploadImgVisible = false" @uploaded="uploaded" /> <upload-img-dialog
<about-dialog v-model="aboutDialogVisible"/> v-model="dialogUploadImgVisible"
<insert-form-dialog v-model="dialogFormVisible"/> @close="dialogUploadImgVisible = false"
@uploaded="uploaded"
/>
<about-dialog v-model="aboutDialogVisible" />
<insert-form-dialog v-model="dialogFormVisible" />
<right-click-menu <right-click-menu
v-model="rightClickMenuVisible" v-model="rightClickMenuVisible"
:left="mouseLeft" :left="mouseLeft"
@ -55,23 +87,23 @@
</div> </div>
</template> </template>
<script> <script>
import editorHeader from '../components/CodemirrorEditor/header'; import editorHeader from "../components/CodemirrorEditor/header";
import aboutDialog from '../components/CodemirrorEditor/aboutDialog'; import aboutDialog from "../components/CodemirrorEditor/aboutDialog";
import insertFormDialog from '../components/CodemirrorEditor/insertForm'; import insertFormDialog from "../components/CodemirrorEditor/insertForm";
import rightClickMenu from '../components/CodemirrorEditor/rightClickMenu'; import rightClickMenu from "../components/CodemirrorEditor/rightClickMenu";
import uploadImgDialog from '../components/CodemirrorEditor/uploadImgDialog'; import uploadImgDialog from "../components/CodemirrorEditor/uploadImgDialog";
import { import {
css2json, css2json,
downLoadMD, downLoadMD,
setFontSize, setFontSize,
saveEditorContent, saveEditorContent,
customCssWithTemplate customCssWithTemplate,
} from '../assets/scripts/util' } from "../assets/scripts/util";
import {uploadImgFile} from '../assets/scripts/uploadImageFile'; import { uploadImgFile } from "../assets/scripts/uploadImageFile";
require('codemirror/mode/javascript/javascript') require("codemirror/mode/javascript/javascript");
import {mapState, mapMutations} from 'vuex'; import { mapState, mapMutations } from "vuex";
export default { export default {
data() { data() {
return { return {
@ -84,29 +116,29 @@ export default {
backLight: false, backLight: false,
timeout: null, timeout: null,
changeTimer: null, changeTimer: null,
source: '', source: "",
mouseLeft: 0, mouseLeft: 0,
mouseTop: 0 mouseTop: 0,
} };
}, },
components: { components: {
editorHeader, editorHeader,
aboutDialog, aboutDialog,
insertFormDialog, insertFormDialog,
rightClickMenu, rightClickMenu,
uploadImgDialog uploadImgDialog,
}, },
computed: { computed: {
...mapState({ ...mapState({
wxRenderer: state => state.wxRenderer, wxRenderer: (state) => state.wxRenderer,
output: state => state.output, output: (state) => state.output,
editor: state => state.editor, editor: (state) => state.editor,
cssEditor: state => state.cssEditor, cssEditor: (state) => state.cssEditor,
currentSize: state => state.currentSize, currentSize: (state) => state.currentSize,
currentColor: state => state.currentColor, currentColor: (state) => state.currentColor,
nightMode: state => state.nightMode, nightMode: (state) => state.nightMode,
rightClickMenuVisible: state => state.rightClickMenuVisible rightClickMenuVisible: (state) => state.rightClickMenuVisible,
}) }),
}, },
created() { created() {
this.initEditorState(); this.initEditorState();
@ -119,43 +151,56 @@ export default {
methods: { methods: {
initEditor() { initEditor() {
this.initEditorEntity(); this.initEditorEntity();
this.editor.on('change', (cm, e) => { this.editor.on("change", (cm, e) => {
if (this.changeTimer) clearTimeout(this.changeTimer); if (this.changeTimer) clearTimeout(this.changeTimer);
this.changeTimer = setTimeout(() => { this.changeTimer = setTimeout(() => {
this.onEditorRefresh(); this.onEditorRefresh();
saveEditorContent(this.editor, '__editor_content'); saveEditorContent(this.editor, "__editor_content");
}, 300); }, 300);
}); });
// //
this.editor.on('paste', (cm, e) => { this.editor.on("paste", (cm, e) => {
if (!(e.clipboardData && e.clipboardData.items) || this.isImgLoading) { if (
!(e.clipboardData && e.clipboardData.items) ||
this.isImgLoading
) {
return; return;
} }
for (let i = 0, len = e.clipboardData.items.length; i < len; ++i) { for (
let i = 0, len = e.clipboardData.items.length;
i < len;
++i
) {
let item = e.clipboardData.items[i]; let item = e.clipboardData.items[i];
if (item.kind === 'file') { if (item.kind === "file") {
// //
const imgHost = localStorage.getItem('imgHost') || 'default'; const imgHost =
if (imgHost != 'default' && !localStorage.getItem(`${imgHost}Config`)) { localStorage.getItem("imgHost") || "default";
if (
imgHost != "default" &&
!localStorage.getItem(`${imgHost}Config`)
) {
this.$message({ this.$message({
showClose: true, showClose: true,
message: '请先配置好图床参数', message: "请先配置好图床参数",
type: 'error' type: "error",
}); });
continue; continue;
} }
this.isImgLoading = true; this.isImgLoading = true;
const pasteFile = item.getAsFile() const pasteFile = item.getAsFile();
uploadImgFile(pasteFile).then(res => { uploadImgFile(pasteFile)
this.uploaded(res) .then((res) => {
}).catch(err => { this.uploaded(res);
})
.catch((err) => {
this.$message({ this.$message({
showClose: true, showClose: true,
message: err, message: err,
type: 'error' type: "error",
}); });
}); });
this.isImgLoading = false; this.isImgLoading = false;
@ -163,38 +208,37 @@ export default {
} }
}); });
this.editor.on('mousedown', () => { this.editor.on("mousedown", () => {
this.$store.commit('setRightClickMenuVisible', false); this.$store.commit("setRightClickMenuVisible", false);
}); });
this.editor.on('blur', () => { this.editor.on("blur", () => {
//!mousedown //!mousedown
this.$store.commit('setRightClickMenuVisible', false); this.$store.commit("setRightClickMenuVisible", false);
}); });
this.editor.on('scroll', () => { this.editor.on("scroll", () => {
this.$store.commit('setRightClickMenuVisible', false); this.$store.commit("setRightClickMenuVisible", false);
}); });
}, },
initCssEditor() { initCssEditor() {
this.initCssEditorEntity(); this.initCssEditorEntity();
// //
this.cssEditor.on('keyup', (cm, e) => { this.cssEditor.on("keyup", (cm, e) => {
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 189) { if ((e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 189) {
cm.showHint(e); cm.showHint(e);
} }
}); });
this.cssEditor.on('update', (instance) => { this.cssEditor.on("update", (instance) => {
this.cssChanged(); this.cssChanged();
saveEditorContent(this.cssEditor, '__css_content'); saveEditorContent(this.cssEditor, "__css_content");
}) });
}, },
cssChanged() { cssChanged() {
let json = css2json(this.cssEditor.getValue(0)); let json = css2json(this.cssEditor.getValue(0));
let theme = setFontSize(this.currentSize.replace('px', '')); let theme = setFontSize(this.currentSize.replace("px", ""));
theme = customCssWithTemplate(json, this.currentColor, theme); theme = customCssWithTemplate(json, this.currentColor, theme);
this.setWxRendererOptions({ this.setWxRendererOptions({
theme: theme theme: theme,
}); });
this.onEditorRefresh(); this.onEditorRefresh();
}, },
@ -203,8 +247,8 @@ export default {
if (!response) { if (!response) {
this.$message({ this.$message({
showClose: true, showClose: true,
message: '上传图片未知异常', message: "上传图片未知异常",
type: 'error' type: "error",
}); });
return; return;
} }
@ -217,47 +261,66 @@ export default {
this.editor.replaceSelection(`\n${markdownImage}\n`, cursor); this.editor.replaceSelection(`\n${markdownImage}\n`, cursor);
this.$message({ this.$message({
showClose: true, showClose: true,
message: '图片上传成功', message: "图片上传成功",
type: 'success' type: "success",
}); });
this.onEditorRefresh(); this.onEditorRefresh();
}, },
// //
leftAndRightScroll() { leftAndRightScroll() {
const scrollCB = text => { const scrollCB = (text) => {
let source, target; let source, target;
clearTimeout(this.timeout); clearTimeout(this.timeout);
if (text === 'preview') { if (text === "preview") {
source = this.$refs.preview.$el; source = this.$refs.preview.$el;
target = document.getElementsByClassName('CodeMirror-scroll')[0]; target = document.getElementsByClassName(
this.editor.off('scroll', editorScrollCB); "CodeMirror-scroll"
)[0];
this.editor.off("scroll", editorScrollCB);
this.timeout = setTimeout(() => { this.timeout = setTimeout(() => {
this.editor.on('scroll', editorScrollCB); this.editor.on("scroll", editorScrollCB);
}, 300); }, 300);
} else if (text === 'editor') { } else if (text === "editor") {
source = document.getElementsByClassName('CodeMirror-scroll')[0]; source = document.getElementsByClassName(
"CodeMirror-scroll"
)[0];
target = this.$refs.preview.$el; target = this.$refs.preview.$el;
target.removeEventListener("scroll", previewScrollCB, false); target.removeEventListener(
"scroll",
previewScrollCB,
false
);
this.timeout = setTimeout(() => { this.timeout = setTimeout(() => {
target.addEventListener("scroll", previewScrollCB, false); target.addEventListener(
"scroll",
previewScrollCB,
false
);
}, 300); }, 300);
} }
let percentage = source.scrollTop / (source.scrollHeight - source.offsetHeight); let percentage =
let height = percentage * (target.scrollHeight - target.offsetHeight); source.scrollTop /
(source.scrollHeight - source.offsetHeight);
let height =
percentage * (target.scrollHeight - target.offsetHeight);
target.scrollTo(0, height); target.scrollTo(0, height);
}; };
const editorScrollCB = () => { const editorScrollCB = () => {
scrollCB('editor'); scrollCB("editor");
}; };
const previewScrollCB = () => { const previewScrollCB = () => {
scrollCB('preview'); scrollCB("preview");
}; };
this.$refs.preview.$el.addEventListener("scroll", previewScrollCB, false); this.$refs.preview.$el.addEventListener(
this.editor.on('scroll', editorScrollCB); "scroll",
previewScrollCB,
false
);
this.editor.on("scroll", editorScrollCB);
}, },
// //
onEditorRefresh() { onEditorRefresh() {
@ -284,43 +347,43 @@ export default {
const left = e.clientX - offsetLeft; const left = e.clientX - offsetLeft;
this.mouseLeft = Math.min(maxLeft, left); this.mouseLeft = Math.min(maxLeft, left);
this.mouseTop = e.clientY + 10; this.mouseTop = e.clientY + 10;
this.$store.commit('setRightClickMenuVisible', true); this.$store.commit("setRightClickMenuVisible", true);
}, },
closeRightClickMenu(){ closeRightClickMenu() {
this.$store.commit('setRightClickMenuVisible', false); this.$store.commit("setRightClickMenuVisible", false);
}, },
onMenuEvent(type, info = {}) { onMenuEvent(type, info = {}) {
switch (type) { switch (type) {
case 'pageReset': case "pageReset":
this.$refs.header.showResetConfirm = true; this.$refs.header.showResetConfirm = true;
break; break;
case 'insertPic': case "insertPic":
this.dialogUploadImgVisible = true this.dialogUploadImgVisible = true;
break; break;
case 'downLoad': case "downLoad":
this.downloadEditorContent(); this.downloadEditorContent();
break; break;
case 'insertTable': case "insertTable":
this.dialogFormVisible = true; this.dialogFormVisible = true;
default: default:
break; break;
} }
}, },
...mapMutations([ ...mapMutations([
'initEditorState', "initEditorState",
'initEditorEntity', "initEditorEntity",
'setWxRendererOptions', "setWxRendererOptions",
'editorRefresh', "editorRefresh",
'initCssEditorEntity']) "initCssEditorEntity",
]),
}, },
mounted() { mounted() {
setTimeout(() => { setTimeout(() => {
this.leftAndRightScroll(); this.leftAndRightScroll();
PR.prettyPrint(); PR.prettyPrint();
}, 300); }, 300);
} },
} };
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.main-body { .main-body {
@ -328,17 +391,17 @@ export default {
overflow: hidden; overflow: hidden;
} }
.el-main { .el-main {
transition: all .3s; transition: all 0.3s;
padding: 0; padding: 0;
margin: 20px; margin: 20px;
margin-top: 0; margin-top: 0;
} }
.container { .container {
transition: all .3s; transition: all 0.3s;
} }
.preview { .preview {
transition: background 0s; transition: background 0s;
transition-delay: .2s; transition-delay: 0.2s;
} }
.preview-wrapper_night { .preview-wrapper_night {
overflow-y: inherit; overflow-y: inherit;
@ -369,7 +432,7 @@ export default {
width: 50px; width: 50px;
height: 50px; height: 50px;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
background: url('../assets/images/favicon.png') no-repeat; background: url("../assets/images/favicon.png") no-repeat;
background-size: cover; background-size: cover;
} }
span { span {
@ -385,30 +448,34 @@ export default {
animation-fill-mode: both; animation-fill-mode: both;
} }
@keyframes bounceInRight { @keyframes bounceInRight {
0%,60%,75%,90%,100% { 0%,
transition-timing-function: cubic-bezier(0.215,.610,.355,1.000) 60%,
75%,
90%,
100% {
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
} }
0% { 0% {
opacity:0; opacity: 0;
transform:translate3d(3000px,0,0)} transform: translate3d(3000px, 0, 0);
}
60% { 60% {
opacity:1; opacity: 1;
transform:translate3d(-25px,0,0) transform: translate3d(-25px, 0, 0);
} }
75% { 75% {
transform:translate3d(10px,0,0) transform: translate3d(10px, 0, 0);
} }
90% { 90% {
transform:translate3d(-5px,0,0) transform: translate3d(-5px, 0, 0);
} }
100% { 100% {
transform:none transform: none;
} }
} }
</style> </style>
<style lang="less"> <style lang="less">
@import url('../assets/less/app.less'); @import url("../assets/less/app.less");
@import url('../assets/less/style-mirror.css'); @import url("../assets/less/style-mirror.css");
@import url('../assets/less/github-v2.min.css'); @import url("../assets/less/github-v2.min.css");
</style> </style>