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,14 +1,14 @@
@import './code-theme.less'; @import "./code-theme.less";
* { * {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
input, input,
button, button,
textarea { textarea {
font-family: inherit; font-family: inherit;
} }
h1, h1,
@ -17,109 +17,110 @@ h3,
h4, h4,
h5, h5,
h6 { h6 {
font-weight: normal; font-weight: normal;
} }
em { em {
font-style: normal !important; font-style: normal !important;
} }
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 {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.top { .top {
height: 60px; height: 60px;
padding: 10px 20px; padding: 10px 20px;
display: flex; display: flex;
align-items: center; align-items: center;
margin-right: 20px; margin-right: 20px;
} }
.web-title { .web-title {
margin: 0 15px 0 5px; margin: 0 15px 0 5px;
} }
.web-icon { .web-icon {
width: auto; width: auto;
height: 1.5rem; height: 1.5rem;
vertical-align: middle; vertical-align: middle;
} }
#editor { #editor {
height: 100%; height: 100%;
display: block; display: block;
border: none; border: none;
width: 100%; width: 100%;
padding: 10px; padding: 10px;
} }
section { section {
height: 100%; height: 100%;
} }
.main-body { .main-body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding-top: 0; padding-top: 0;
padding-bottom: 10px; padding-bottom: 10px;
} }
.ctrl { .ctrl {
flex-basis: 60px; flex-basis: 60px;
flex-grow: 1; flex-grow: 1;
flex-shrink: 1; flex-shrink: 1;
display: flex; display: flex;
align-items: center; align-items: center;
} }
.preview-wrapper { .preview-wrapper {
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
padding: 0; padding: 0;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
display: flex; display: flex;
overflow: scroll; overflow: scroll;
word-break: break-all; word-break: break-all;
} }
.main-section { .main-section {
display: flex; display: flex;
height: 100%; height: 100%;
} }
.hint { .hint {
opacity: 0.6; opacity: 0.6;
margin: 20px 0; margin: 20px 0;
} }
.preview { .preview {
margin: 0 -20px; margin: 0 -20px;
width: 375px; width: 375px;
padding: 20px; padding: 20px;
font-size: 14px; font-size: 14px;
box-sizing: border-box; box-sizing: border-box;
outline: none; outline: none;
box-shadow: 0 0 60px rgba(0, 0, 0, 0.1); box-shadow: 0 0 60px rgba(0, 0, 0, 0.1);
} }
.preview table { .preview table {
margin-bottom: 10px; margin-bottom: 10px;
border-collapse: collapse; border-collapse: collapse;
display: table; display: table;
width: 100% !important; width: 100% !important;
} }
/* /*
@ -128,60 +129,61 @@ section {
} }
*/ */
.select-item-left { .select-item-left {
float: left; float: left;
} }
.select-item-right { .select-item-right {
float: right; float: right;
color: #8492a6; color: #8492a6;
font-size: 13px; font-size: 13px;
} }
.CodeMirror { .CodeMirror {
height: 100% !important; height: 100% !important;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
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 */
.el-form-item { .el-form-item {
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
.el-tooltip { .el-tooltip {
cursor: pointer; cursor: pointer;
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 6px; width: 6px;
height: 6px; height: 6px;
background-color: #FFF; background-color: #fff;
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
border-radius: 6px; border-radius: 6px;
background-color: rgba(200, 200, 200, 0.3); background-color: rgba(200, 200, 200, 0.3);
} }
::-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 {
background-color: rgba(144, 146, 152, 0.5); background-color: rgba(144, 146, 152, 0.5);
} }
.CodeMirror-vscrollbar:focus { .CodeMirror-vscrollbar:focus {
outline: none; outline: none;
} }
.CodeMirror-scroll, .CodeMirror-scroll,
.preview-wrapper { .preview-wrapper {
overflow: unset; overflow: unset;
overflow-y: scroll; overflow-y: scroll;
} }

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

@ -1,47 +1,47 @@
/*github code block*/ /*github code block*/
.code-snippet__github { .code-snippet__github {
display: flex; display: flex;
font-size: 12px;
margin: 10px 8px;
position: relative;
height: auto;
background-color: rgba(27, 31, 35, 0.05);
.code-snippet__line-index {
display: none;
}
.code__pre {
display: grid;
position: relative;
counter-reset: line;
overflow-x: auto;
padding: 1em;
white-space: normal;
flex: 1;
line-height: 20px;
font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;
-webkit-overflow-scrolling: touch;
}
pre {
display: inline-block;
font-size: 12px; font-size: 12px;
margin: 10px 8px; }
position: relative;
height: auto;
background-color: rgba(27,31,35,.05);
.code-snippet__line-index { code {
display: none; display: flex;
position: relative;
padding-right: 8px;
text-align: left;
white-space: pre;
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
&::before {
display: none;
} }
}
.code__pre { ul li {
display: grid; list-style: none;
position: relative; }
counter-reset: line; }
overflow-x: auto;
padding: 1em;
white-space: normal;
flex: 1;
line-height: 20px;
font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;
-webkit-overflow-scrolling: touch;
}
pre {
display: inline-block;
font-size: 12px;
}
code {
display: flex;
position: relative;
padding-right: 8px;
text-align: left;
white-space: pre;
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
&::before {
display: none;
}
}
ul li {
list-style: none;
}
}

View File

@ -1,62 +1,62 @@
/*wechat code block*/ /*wechat code block*/
.rich_media_content .code-snippet *, .rich_media_content .code-snippet *,
.rich_media_content .code-snippet__wechat * { .rich_media_content .code-snippet__wechat * {
max-width: 1000% !important; max-width: 1000% !important;
} }
.code-snippet__wechat { .code-snippet__wechat {
word-wrap: break-word !important; word-wrap: break-word !important;
font-size: 14px; font-size: 14px;
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;
line-height: 24px; line-height: 24px;
} }
.code-snippet__wechat .code-snippet__line-index { .code-snippet__wechat .code-snippet__line-index {
counter-reset: line; counter-reset: line;
flex-shrink: 0; flex-shrink: 0;
height: 100%; height: 100%;
padding: 1em; padding: 1em;
list-style-type: none; list-style-type: none;
} }
.code-snippet__wechat .code-snippet__line-index li { .code-snippet__wechat .code-snippet__line-index li {
list-style-type: none; list-style-type: none;
text-align: right; text-align: right;
} }
.code-snippet__wechat .code-snippet__line-index li::before { .code-snippet__wechat .code-snippet__line-index li::before {
min-width: 1.5em; min-width: 1.5em;
text-align: right; text-align: right;
left: -2.5em; left: -2.5em;
counter-increment: line; counter-increment: line;
content: counter(line); content: counter(line);
display: inline; display: inline;
color: rgba(0, 0, 0, 0.15); color: rgba(0, 0, 0, 0.15);
} }
.code-snippet__wechat pre { .code-snippet__wechat pre {
overflow-x: auto; overflow-x: auto;
padding: 1em 1em 1em 1em; padding: 1em 1em 1em 1em;
white-space: normal; white-space: normal;
flex: 1; flex: 1;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
.code-snippet__wechat code { .code-snippet__wechat code {
text-align: left; text-align: left;
font-size: 14px; font-size: 14px;
white-space: pre; white-space: pre;
display: flex; display: flex;
position: relative; position: relative;
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
} }
.code-snippet__wechat ul li { .code-snippet__wechat ul li {
list-style: none; list-style: none;
} }

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

@ -9,116 +9,116 @@
*/ */
.cm-s-style-mirror.CodeMirror { .cm-s-style-mirror.CodeMirror {
background: #f5f5f5; background: #f5f5f5;
color: #444; color: #444;
font-size: 16px; font-size: 16px;
padding: 20px; padding: 20px;
line-height: 25px; line-height: 25px;
} }
.cm-s-style-mirror div.CodeMirror-selected { .cm-s-style-mirror div.CodeMirror-selected {
background: #e0e0e0; background: #e0e0e0;
} }
.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;
} }
.cm-s-style-mirror .CodeMirror-gutters { .cm-s-style-mirror .CodeMirror-gutters {
background: #f5f5f5; background: #f5f5f5;
border-right: 0px; border-right: 0px;
} }
.cm-s-style-mirror .CodeMirror-guttermarker { .cm-s-style-mirror .CodeMirror-guttermarker {
color: #ac4142; color: #ac4142;
} }
.cm-s-style-mirror .CodeMirror-guttermarker-subtle { .cm-s-style-mirror .CodeMirror-guttermarker-subtle {
color: #b0b0b0; color: #b0b0b0;
} }
.cm-s-style-mirror .CodeMirror-linenumber { .cm-s-style-mirror .CodeMirror-linenumber {
color: #b0b0b0; color: #b0b0b0;
} }
.cm-s-style-mirror .CodeMirror-cursor { .cm-s-style-mirror .CodeMirror-cursor {
border-left: 1px solid #505050; border-left: 1px solid #505050;
} }
.cm-s-style-mirror span.cm-comment { .cm-s-style-mirror span.cm-comment {
color: green; color: green;
} }
.cm-s-style-mirror span.cm-atom { .cm-s-style-mirror span.cm-atom {
color: #aa759f; color: #aa759f;
} }
.cm-s-style-mirror span.cm-number { .cm-s-style-mirror span.cm-number {
color: #aa759f; color: #aa759f;
} }
.cm-s-style-mirror span.cm-property, .cm-s-style-mirror span.cm-property,
.cm-s-style-mirror span.cm-attribute { .cm-s-style-mirror span.cm-attribute {
color: #90a959; color: #90a959;
} }
.cm-s-style-mirror span.cm-keyword { .cm-s-style-mirror span.cm-keyword {
color: #023a52; color: #023a52;
} }
.cm-s-style-mirror span.cm-string { .cm-s-style-mirror span.cm-string {
color: #e46918; color: #e46918;
} }
.cm-s-style-mirror span.cm-variable { .cm-s-style-mirror span.cm-variable {
color: #90a959; color: #90a959;
} }
.cm-s-style-mirror span.cm-variable-2 { .cm-s-style-mirror span.cm-variable-2 {
color: #00695f; color: #00695f;
} }
.cm-s-style-mirror span.cm-variable-3 { .cm-s-style-mirror span.cm-variable-3 {
color: #2e6e8a; color: #2e6e8a;
} }
.cm-s-style-mirror span.cm-def { .cm-s-style-mirror span.cm-def {
color: #d28445; color: #d28445;
} }
.cm-s-style-mirror span.cm-bracket { .cm-s-style-mirror span.cm-bracket {
color: #202020; color: #202020;
} }
.cm-s-style-mirror span.cm-tag { .cm-s-style-mirror span.cm-tag {
color: #000; color: #000;
} }
.cm-s-style-mirror span.cm-link { .cm-s-style-mirror span.cm-link {
color: #b26a00; color: #b26a00;
} }
.cm-s-style-mirror span.cm-error { .cm-s-style-mirror span.cm-error {
/* background: #ac4142; /* background: #ac4142;
color: #f5f5f5; */ color: #f5f5f5; */
text-decoration: underline; text-decoration: underline;
text-decoration-style: wavy; text-decoration-style: wavy;
text-decoration-color: #df8d8e; text-decoration-color: #df8d8e;
} }
.cm-s-style-mirror .CodeMirror-activeline-background { .cm-s-style-mirror .CodeMirror-activeline-background {
background: #dddcdc; background: #dddcdc;
} }
.cm-s-style-mirror .CodeMirror-matchingbracket { .cm-s-style-mirror .CodeMirror-matchingbracket {
color: rgb(32, 32, 32) !important; color: rgb(32, 32, 32) !important;
background-color: rgba(0, 0, 0, 0.1) !important; background-color: rgba(0, 0, 0, 0.1) !important;
} }

View File

@ -10,85 +10,94 @@
@nightButtonBg: #1e1e1e; @nightButtonBg: #1e1e1e;
@nightButtonHoverColor: #84868b; @nightButtonHoverColor: #84868b;
.container_night { .container_night {
background-color: @nightBgColor;
.el-main {
background-color: @nightBgColor; background-color: @nightBgColor;
.el-main { }
background-color: @nightBgColor; .CodeMirror {
caret-color: @nightFontColor;
color: @nightFontColor;
background-color: @nightCodeMirrorColor;
box-shadow: inset 0 0 0 1px rgba(100, 37, 37, 0.102);
}
.output_night {
.preview {
background-color: @nightPreviewColor;
box-shadow: 0 0 70px rgba(0, 0, 0, 0.3);
} }
.CodeMirror { .preview-wrapper {
caret-color: @nightFontColor; background-color: @nightCodeMirrorColor;
color: @nightFontColor; box-shadow: inset 0 0 0 1px rgba(233, 231, 231, 0.102);
background-color: @nightCodeMirrorColor;
box-shadow: inset 0 0 0 1px rgba(100, 37, 37, 0.102);
} }
.output_night { .code-snippet__fix {
.preview { background-color: rgb(238, 238, 238);
background-color: @nightPreviewColor;
box-shadow: 0 0 70px rgba(0, 0, 0, 0.3);
}
.preview-wrapper {
background-color: @nightCodeMirrorColor;
box-shadow: inset 0 0 0 1px rgba(233, 231, 231, 0.102);
}
.code-snippet__fix {
background-color: rgb(238,238,238);
}
} }
.cm-s-style-mirror .CodeMirror-matchingbracket { }
color: @nightWhiteColor!important; .cm-s-style-mirror .CodeMirror-matchingbracket {
background: rgb(30, 30, 30)!important; color: @nightWhiteColor!important;
background: rgb(30, 30, 30) !important;
}
.cm-s-xq-light span.cm-variable-2,
.cm-s-style-mirror span.cm-tag {
color: @nightFontColor;
}
.cm-s-xq-light .CodeMirror-activeline-background {
background-color: transparent;
}
.cm-s-xq-light span.cm-string {
color: @nightLinkColor;
}
.cm-s-xq-light span.cm-link {
color: @nightLinkTextColor;
}
.editor__header {
background-color: @nightHeaderColor;
}
.el-button {
color: @nightWhiteColor;
background-color: @nightCodeMirrorColor;
border: 1px solid transparent;
}
.el-button.is-plain:focus,
.el-button.is-plain:hover {
background: @nightButtonBg;
color: @nightWhiteColor;
border: 1px solid @nightWhiteColor;
i {
color: @nightWhiteColor;
} }
.cm-s-xq-light span.cm-variable-2, .cm-s-style-mirror span.cm-tag { }
color: @nightFontColor; .insert__dialog,
.about__dialog,
.reset__dialog,
.upload__dialog {
.el-dialog {
background-color: @nightBgColor;
} }
.cm-s-xq-light .CodeMirror-activeline-background { .el-dialog__body {
background-color: transparent; color: @nightWhiteColor;
} }
.cm-s-xq-light span.cm-string { .el-dialog__title,
color: @nightLinkColor; .el-form-item__label {
color: @nightWhiteColor;
} }
.cm-s-xq-light span.cm-link { .el-tabs__item {
color: @nightLinkTextColor; color: @nightActiveCodeMirrorColor;
} }
.editor__header { .is-active {
background-color: @nightHeaderColor; color: @nightWhiteColor;
} }
.el-button { .el-upload-dragger {
color: @nightWhiteColor; background-color: @nightButtonBg;
background-color: @nightCodeMirrorColor;
border: 1px solid transparent;
} }
.el-button.is-plain:focus, .el-button.is-plain:hover { }
background: @nightButtonBg; ::v-deep .el-icon-upload,
color: @nightWhiteColor; .el-icon-download,
border: 1px solid @nightWhiteColor; .el-icon-refresh,
i { .el-icon-s-grid {
color: @nightWhiteColor; color: @nightWhiteColor;
} }
} ::-webkit-scrollbar {
.insert__dialog, .about__dialog, .reset__dialog, .upload__dialog { background-color: @nightCodeMirrorColor;
.el-dialog { }
background-color: @nightBgColor; }
}
.el-dialog__body {
color: @nightWhiteColor;
}
.el-dialog__title, .el-form-item__label {
color: @nightWhiteColor;
}
.el-tabs__item {
color: @nightActiveCodeMirrorColor;
}
.is-active {
color: @nightWhiteColor;
}
.el-upload-dragger {
background-color: @nightButtonBg;
}
}
::v-deep .el-icon-upload, .el-icon-download, .el-icon-refresh, .el-icon-s-grid {
color: @nightWhiteColor;
}
::-webkit-scrollbar {
background-color: @nightCodeMirrorColor;
}
}

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
</el-container> :showResetConfirm="showResetConfirm"
@confirm="confirmReset"
@close="cancelReset"
/>
</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,15 +377,15 @@ 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 {
margin-right: 0; margin-right: 0;
} }
</style> </style>

View File

@ -6,66 +6,71 @@
@close="$emit('input', false)" @close="$emit('input', false)"
border border
> >
<el-row class="tb-options" type="flex" align="middle" :gutter="10"> <el-row class="tb-options" type="flex" align="middle" :gutter="10">
<el-col> <el-col>
行数 行数
<el-input-number <el-input-number
v-model="rowNum" v-model="rowNum"
controls-position="right" controls-position="right"
:min="1" :min="1"
:max="100" :max="100"
size="small" size="small"
></el-input-number> ></el-input-number>
</el-col> </el-col>
<el-col> <el-col>
列数 列数
<el-input-number <el-input-number
v-model="colNum" v-model="colNum"
controls-position="right" controls-position="right"
:min="1" :min="1"
:max="100" :max="100"
size="small" size="small"
></el-input-number> ></el-input-number>
</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
<td v-for="col in colNum" :key="col"> :class="{ 'head-style': row === 1 }"
<el-input v-for="row in rowNum + 1"
align="center" :key="row"
v-model="tableData[`k_${row-1}_${col-1}`]" >
:placeholder="row===1?'表头':''" <td v-for="col in colNum" :key="col">
/> <el-input
</td> align="center"
</tr> v-model="tableData[`k_${row - 1}_${col - 1}`]"
</table> :placeholder="row === 1 ? '表头' : ''"
<div slot="footer" class="dialog-footer"> />
<el-button :type="btnType" plain @click="$emit('input', false)"> </el-button> </td>
<el-button :type="btnType" @click="insertTable" plain> </el-button> </tr>
</div> </table>
</el-dialog> <div slot="footer" class="dialog-footer">
<el-button :type="btnType" plain @click="$emit('input', false)"
> </el-button
>
<el-button :type="btnType" @click="insertTable" plain
> </el-button
>
</div>
</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'; props: {
export default { showResetConfirm: {
props: { type: Boolean,
showResetConfirm: { default: false,
type: Boolean,
default: false
}
}, },
computed: { },
btnType() { computed: {
return this.nightMode ? 'default' : 'primary'; btnType() {
}, return this.nightMode ? "default" : "primary";
...mapState({ },
nightMode: state => state.nightMode ...mapState({
}) 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 {
text-align: center;
}
.dialog-footer {
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) {
this.$emit("menuTick", key);
this.$emit("closeMenu", false);
}, },
onMouseDown(key){
this.$emit('menuTick', key)
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,323 +1,452 @@
<template> <template>
<el-dialog title="本地上传" class="upload__dialog" :visible="value" @close="$emit('close')"> <el-dialog
<el-tabs type="card" :value="'upload'"> title="本地上传"
<el-tab-pane class="upload-panel" label="选择上传" name="upload"> class="upload__dialog"
<el-select v-model="imgHost" @change="changeImgHost" placeholder="请选择" size="small"> :visible="value"
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> @close="$emit('close')"
</el-option> >
</el-select> <el-tabs type="card" :value="'upload'">
<el-upload drag action :headers="{'Content-Type': 'multipart/form-data'}" :show-file-list="false" <el-tab-pane class="upload-panel" label="选择上传" name="upload">
:multiple="true" accept=".jpg, .jpeg, .png, .gif" name="file" :before-upload="beforeUpload" <el-select
v-loading="uploadingImg"> v-model="imgHost"
<i class="el-icon-upload"></i> @change="changeImgHost"
<div class="el-upload__text"> placeholder="请选择"
将图片拖到此处 size="small"
<em>点击上传</em> >
</div> <el-option
</el-upload> v-for="item in options"
</el-tab-pane> :key="item.value"
<el-tab-pane class="github-panel" label="GitHub 图床" name="github"> :label="item.label"
<el-form class="setting-form" ref="form" :model="formGitHub" label-position="right" label-width="140px"> :value="item.value"
<el-form-item label="GitHub 仓库" :required="true"> >
<el-input v-model.trim="formGitHub.repo" placeholder="如github.com/yanglbme/resource"></el-input> </el-option>
</el-form-item> </el-select>
<el-form-item label="分支"> <el-upload
<el-input v-model.trim="formGitHub.branch" placeholder="如release可不填默认 master"></el-input> drag
</el-form-item> action
<el-form-item label="Token" :required="true"> :headers="{ 'Content-Type': 'multipart/form-data' }"
<el-input v-model.trim="formGitHub.accessToken" show-password :show-file-list="false"
placeholder="如cc1d0c1426d0fd0902bd2d7184b14da61b8abc46"></el-input> :multiple="true"
<el-link type="primary" accept=".jpg, .jpeg, .png, .gif"
href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token" name="file"
target="_blank">如何获取 GitHub Token</el-link> :before-upload="beforeUpload"
</el-form-item> v-loading="uploadingImg"
<el-form-item> >
<el-button type="primary" @click="saveGitHubConfiguration">保存配置</el-button> <i class="el-icon-upload"></i>
</el-form-item> <div class="el-upload__text">
</el-form> 将图片拖到此处
</el-tab-pane> <em>点击上传</em>
<el-tab-pane class="github-panel" label="阿里云 OSS" name="aliOSS"> </div>
<el-form class="setting-form" ref="form" :model="formAliOSS" label-position="right" label-width="140px"> </el-upload>
<el-form-item label="AccessKey ID" :required="true"> </el-tab-pane>
<el-input v-model.trim="formAliOSS.accessKeyId" placeholder="如LTAI4GdoocsmdoxUf13ylbaNHk"></el-input> <el-tab-pane class="github-panel" label="GitHub 图床" name="github">
</el-form-item> <el-form
<el-form-item label="AccessKey Secret" :required="true"> class="setting-form"
<el-input v-model.trim="formAliOSS.accessKeySecret" show-password ref="form"
placeholder="如cc1d0c142doocs0902bd2d7md4b14da6ylbabc46"></el-input> :model="formGitHub"
</el-form-item> label-position="right"
<el-form-item label="Bucket" :required="true"> label-width="140px"
<el-input v-model.trim="formAliOSS.bucket" >
placeholder="如doocs"></el-input> <el-form-item label="GitHub 仓库" :required="true">
</el-form-item> <el-input
<el-form-item label="Bucket 所在区域" :required="true"> v-model.trim="formGitHub.repo"
<el-input v-model.trim="formAliOSS.region" placeholder="如github.com/yanglbme/resource"
placeholder="如oss-cn-shenzhen"></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="自定义 CDN 域名" :required="false"> <el-form-item label="分支">
<el-input v-model.trim="formAliOSS.cdnHost" <el-input
placeholder="如https://imagecdn.alidaodao.com可不填"></el-input> v-model.trim="formGitHub.branch"
</el-form-item> placeholder="如release可不填默认 master"
<el-form-item label="存储路径"> ></el-input>
<el-input v-model.trim="formAliOSS.path" </el-form-item>
placeholder="如img可不填默认为根目录"></el-input> <el-form-item label="Token" :required="true">
<el-link type="primary" <el-input
href="https://help.aliyun.com/document_detail/31883.html" v-model.trim="formGitHub.accessToken"
target="_blank">如何使用阿里云 OSS</el-link> show-password
</el-form-item> placeholder="如cc1d0c1426d0fd0902bd2d7184b14da61b8abc46"
<el-form-item> ></el-input>
<el-button type="primary" @click="saveAliOSSConfiguration">保存配置</el-button> <el-link
</el-form-item> type="primary"
</el-form> href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token"
</el-tab-pane> target="_blank"
<el-tab-pane class="github-panel" label="腾讯云 COS" name="txCOS"> >如何获取 GitHub Token</el-link
<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>
<el-input v-model.trim="formTxCOS.secretId" placeholder="如AKIDnQp1w3DOOCSs8F5MDp9tdoocsmdUPonW3"></el-input> <el-form-item>
</el-form-item> <el-button
<el-form-item label="SecretKey" :required="true"> type="primary"
<el-input v-model.trim="formTxCOS.secretKey" show-password @click="saveGitHubConfiguration"
placeholder="如ukLmdtEJ9271f3DOocsMDsCXdS3YlbW0"></el-input> >保存配置</el-button
</el-form-item> >
<el-form-item label="Bucket" :required="true"> </el-form-item>
<el-input v-model.trim="formTxCOS.bucket" </el-form>
placeholder="如doocs-3212520134"></el-input> </el-tab-pane>
</el-form-item> <el-tab-pane class="github-panel" label="阿里云 OSS" name="aliOSS">
<el-form-item label="Bucket 所在区域" :required="true"> <el-form
<el-input v-model.trim="formTxCOS.region" class="setting-form"
placeholder="如ap-guangzhou"></el-input> ref="form"
</el-form-item> :model="formAliOSS"
<el-form-item label="自定义 CDN 域名" :required="false"> label-position="right"
<el-input v-model.trim="formTxCOS.cdnHost" label-width="140px"
placeholder="如https://imagecdn.alidaodao.com可不填"></el-input> >
</el-form-item> <el-form-item label="AccessKey ID" :required="true">
<el-form-item label="存储路径"> <el-input
<el-input v-model.trim="formTxCOS.path" v-model.trim="formAliOSS.accessKeyId"
placeholder="如img可不填默认根目录"></el-input> placeholder="如LTAI4GdoocsmdoxUf13ylbaNHk"
<el-link type="primary" ></el-input>
href="https://cloud.tencent.com/document/product/436/38484" </el-form-item>
target="_blank">如何使用腾讯云 COS</el-link> <el-form-item label="AccessKey Secret" :required="true">
</el-form-item> <el-input
<el-form-item> v-model.trim="formAliOSS.accessKeySecret"
<el-button type="primary" @click="saveTxCOSConfiguration">保存配置</el-button> show-password
</el-form-item> placeholder="如cc1d0c142doocs0902bd2d7md4b14da6ylbabc46"
</el-form> ></el-input>
</el-tab-pane> </el-form-item>
</el-tabs> <el-form-item label="Bucket" :required="true">
</el-dialog> <el-input
</template> v-model.trim="formAliOSS.bucket"
placeholder="如doocs"
<script> ></el-input>
import { </el-form-item>
uploadImgFile <el-form-item label="Bucket 所在区域" :required="true">
} from "../../assets/scripts/uploadImageFile"; <el-input
v-model.trim="formAliOSS.region"
export default { placeholder="如oss-cn-shenzhen"
props: { ></el-input>
value: { </el-form-item>
type: Boolean, <el-form-item label="自定义 CDN 域名" :required="false">
default: false, <el-input
}, v-model.trim="formAliOSS.cdnHost"
}, placeholder="如https://imagecdn.alidaodao.com可不填"
data() { ></el-input>
return { </el-form-item>
formGitHub: { <el-form-item label="存储路径">
repo: "", <el-input
branch: "", v-model.trim="formAliOSS.path"
accessToken: "", placeholder="如img可不填默认为根目录"
}, ></el-input>
formAliOSS: { <el-link
accessKeyId: "", type="primary"
accessKeySecret: "", href="https://help.aliyun.com/document_detail/31883.html"
bucket: "", target="_blank"
region: "", >如何使用阿里云 OSS</el-link
path: "", >
cdnHost: "", </el-form-item>
}, <el-form-item>
formTxCOS: { <el-button
secretId: "", type="primary"
secretKey: "", @click="saveAliOSSConfiguration"
bucket: "", >保存配置</el-button
region: "", >
path: "", </el-form-item>
cdnHost: "", </el-form>
}, </el-tab-pane>
options: [{ <el-tab-pane class="github-panel" label="腾讯云 COS" name="txCOS">
value: "default", <el-form
label: "默认图床", class="setting-form"
}, ref="form"
{ :model="formTxCOS"
value: "github", label-position="right"
label: "GitHub", label-width="140px"
}, >
{ <el-form-item label="SecretId" :required="true">
value: "aliOSS", <el-input
label: "阿里云" v-model.trim="formTxCOS.secretId"
}, placeholder="如AKIDnQp1w3DOOCSs8F5MDp9tdoocsmdUPonW3"
{ ></el-input>
value: "txCOS", </el-form-item>
label: "腾讯云" <el-form-item label="SecretKey" :required="true">
} <el-input
], v-model.trim="formTxCOS.secretKey"
imgHost: "default", show-password
uploadingImg: false, placeholder="如ukLmdtEJ9271f3DOocsMDsCXdS3YlbW0"
}; ></el-input>
}, </el-form-item>
created() { <el-form-item label="Bucket" :required="true">
if (localStorage.getItem("githubConfig")) { <el-input
this.formGitHub = JSON.parse(localStorage.getItem("githubConfig")); v-model.trim="formTxCOS.bucket"
} placeholder="如doocs-3212520134"
if (localStorage.getItem("aliOSSConfig")) { ></el-input>
this.formAliOSS = JSON.parse(localStorage.getItem("aliOSSConfig")); </el-form-item>
} <el-form-item label="Bucket 所在区域" :required="true">
if (localStorage.getItem("txCOSConfig")) { <el-input
this.formTxCOS = JSON.parse(localStorage.getItem("txCOSConfig")); v-model.trim="formTxCOS.region"
} placeholder="如ap-guangzhou"
if (localStorage.getItem("imgHost")) { ></el-input>
this.imgHost = localStorage.getItem("imgHost"); </el-form-item>
} <el-form-item label="自定义 CDN 域名" :required="false">
}, <el-input
methods: { v-model.trim="formTxCOS.cdnHost"
changeImgHost() { placeholder="如https://imagecdn.alidaodao.com可不填"
localStorage.setItem("imgHost", this.imgHost); ></el-input>
this.$message({ </el-form-item>
showClose: true, <el-form-item label="存储路径">
message: '已成功切换图床', <el-input
type: "success", v-model.trim="formTxCOS.path"
}); placeholder="如img可不填默认根目录"
}, ></el-input>
saveGitHubConfiguration() { <el-link
if (!(this.formGitHub.repo && this.formGitHub.accessToken)) { type="primary"
const blankElement = this.formGitHub.repo ? "token" : "GitHub 仓库" href="https://cloud.tencent.com/document/product/436/38484"
this.$message({ target="_blank"
showClose: true, >如何使用腾讯云 COS</el-link
message: `参数「​${blankElement}」不能为空`, >
type: "error", </el-form-item>
}); <el-form-item>
return; <el-button
} type="primary"
localStorage.setItem("githubConfig", JSON.stringify(this.formGitHub)); @click="saveTxCOSConfiguration"
this.$message({ >保存配置</el-button
message: "保存成功", >
type: "success", </el-form-item>
}); </el-form>
}, </el-tab-pane>
saveAliOSSConfiguration() { </el-tabs>
if (!(this.formAliOSS.accessKeyId && this.formAliOSS.accessKeySecret && this.formAliOSS.bucket && this.formAliOSS.region)) { </el-dialog>
this.$message({ </template>
showClose: true,
message: `阿里云 OSS 参数配置不全`, <script>
type: "error", import { uploadImgFile } from "../../assets/scripts/uploadImageFile";
});
return; export default {
} props: {
localStorage.setItem("aliOSSConfig", JSON.stringify(this.formAliOSS)); value: {
this.$message({ type: Boolean,
message: "保存成功", default: false,
type: "success", },
}); },
}, data() {
return {
saveTxCOSConfiguration() { formGitHub: {
if (!(this.formTxCOS.secretId && this.formTxCOS.secretKey && this.formTxCOS.bucket && this.formTxCOS.region)) { repo: "",
this.$message({ branch: "",
showClose: true, accessToken: "",
message: `腾讯云 COS 参数配置不全`, },
type: "error", formAliOSS: {
}); accessKeyId: "",
return; accessKeySecret: "",
} bucket: "",
localStorage.setItem("txCOSConfig", JSON.stringify(this.formTxCOS)); region: "",
this.$message({ path: "",
message: "保存成功", cdnHost: "",
type: "success", },
}); formTxCOS: {
}, secretId: "",
secretKey: "",
// bucket: "",
beforeUpload(file) { region: "",
if (!this.validateConfig()) { path: "",
return; cdnHost: "",
} },
options: [
this.uploadingImg = true; {
uploadImgFile(file) value: "default",
.then(res => { label: "默认图床",
this.$emit("uploaded", res); },
this.uploadingImg = false; {
}) value: "github",
.catch(err => { label: "GitHub",
this.uploadingImg = false; },
this.$message({ {
showClose: true, value: "aliOSS",
message: err, label: "阿里云",
type: "error", },
}); {
}); value: "txCOS",
return false; label: "腾讯云",
}, },
validateConfig() { ],
let checkRes = true, errMessage = ''; imgHost: "default",
uploadingImg: false,
switch (localStorage.getItem('imgHost')) { };
case 'github': },
checkRes = this.formGitHub.repo && this.formGitHub.accessToken; created() {
errMessage = checkRes ? '' : '请先配置 GitHub 图床参数'; if (localStorage.getItem("githubConfig")) {
break; this.formGitHub = JSON.parse(localStorage.getItem("githubConfig"));
case 'aliOSS': }
checkRes = this.formAliOSS.accessKeyId && this.formAliOSS.accessKeySecret && this.formAliOSS.bucket && this.formAliOSS.region; if (localStorage.getItem("aliOSSConfig")) {
errMessage = checkRes ? '' : '请先配置阿里云 OSS 参数'; this.formAliOSS = JSON.parse(localStorage.getItem("aliOSSConfig"));
break; }
case 'txCOS': if (localStorage.getItem("txCOSConfig")) {
checkRes = this.formTxCOS.secretId && this.formTxCOS.secretKey && this.formTxCOS.bucket && this.formTxCOS.region; this.formTxCOS = JSON.parse(localStorage.getItem("txCOSConfig"));
errMessage = checkRes ? '' : '请先配置腾讯云 COS 参数'; }
break; if (localStorage.getItem("imgHost")) {
} this.imgHost = localStorage.getItem("imgHost");
errMessage && this.$message.error(errMessage); }
return checkRes; },
}, methods: {
}, changeImgHost() {
}; localStorage.setItem("imgHost", this.imgHost);
</script> this.$message({
showClose: true,
<style lang="less" scoped> message: "已成功切换图床",
/deep/ .el-dialog { type: "success",
width: 40%; });
} },
/deep/ .el-upload-dragger { saveGitHubConfiguration() {
width: 335px; if (!(this.formGitHub.repo && this.formGitHub.accessToken)) {
} const blankElement = this.formGitHub.repo
/deep/ .el-dialog__body { ? "token"
padding-bottom: 50px; : "GitHub 仓库";
} this.$message({
.upload-panel { showClose: true,
display: flex; message: `参数「​${blankElement}」不能为空`,
flex-direction: column; type: "error",
justify-content: center; });
align-items: center; return;
text-align: center; }
localStorage.setItem(
.el-select { "githubConfig",
align-self: flex-end; JSON.stringify(this.formGitHub)
margin: 0 67.75px 20px; );
width: 100px; this.$message({
} message: "保存成功",
} type: "success",
});
.github-panel { },
display: flex; saveAliOSSConfiguration() {
justify-content: center; if (
} !(
this.formAliOSS.accessKeyId &&
.setting-form { this.formAliOSS.accessKeySecret &&
width: 100%; this.formAliOSS.bucket &&
this.formAliOSS.region
.el-form-item { )
margin: 15px; ) {
} this.$message({
showClose: true,
.el-form-item:last-child { message: `阿里云 OSS 参数配置不全`,
text-align: right; type: "error",
} });
} return;
}
</style> localStorage.setItem(
"aliOSSConfig",
JSON.stringify(this.formAliOSS)
);
this.$message({
message: "保存成功",
type: "success",
});
},
saveTxCOSConfiguration() {
if (
!(
this.formTxCOS.secretId &&
this.formTxCOS.secretKey &&
this.formTxCOS.bucket &&
this.formTxCOS.region
)
) {
this.$message({
showClose: true,
message: `腾讯云 COS 参数配置不全`,
type: "error",
});
return;
}
localStorage.setItem("txCOSConfig", JSON.stringify(this.formTxCOS));
this.$message({
message: "保存成功",
type: "success",
});
},
//
beforeUpload(file) {
if (!this.validateConfig()) {
return;
}
this.uploadingImg = true;
uploadImgFile(file)
.then((res) => {
this.$emit("uploaded", res);
this.uploadingImg = false;
})
.catch((err) => {
this.uploadingImg = false;
this.$message({
showClose: true,
message: err,
type: "error",
});
});
return false;
},
validateConfig() {
let checkRes = true,
errMessage = "";
switch (localStorage.getItem("imgHost")) {
case "github":
checkRes =
this.formGitHub.repo && this.formGitHub.accessToken;
errMessage = checkRes ? "" : "请先配置 GitHub 图床参数";
break;
case "aliOSS":
checkRes =
this.formAliOSS.accessKeyId &&
this.formAliOSS.accessKeySecret &&
this.formAliOSS.bucket &&
this.formAliOSS.region;
errMessage = checkRes ? "" : "请先配置阿里云 OSS 参数";
break;
case "txCOS":
checkRes =
this.formTxCOS.secretId &&
this.formTxCOS.secretKey &&
this.formTxCOS.bucket &&
this.formTxCOS.region;
errMessage = checkRes ? "" : "请先配置腾讯云 COS 参数";
break;
}
errMessage && this.$message.error(errMessage);
return checkRes;
},
},
};
</script>
<style lang="less" scoped>
/deep/ .el-dialog {
width: 40%;
}
/deep/ .el-upload-dragger {
width: 335px;
}
/deep/ .el-dialog__body {
padding-bottom: 50px;
}
.upload-panel {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
.el-select {
align-self: flex-end;
margin: 0 67.75px 20px;
width: 100px;
}
}
.github-panel {
display: flex;
justify-content: center;
}
.setting-form {
width: 100%;
.el-form-item {
margin: 15px;
}
.el-form-item:last-child {
text-align: right;
}
}
</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
</textarea> id="cssEditor"
type="textarea"
placeholder="Your custom css here."
>
</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,82 +151,94 @@ 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);
this.$message({ })
showClose: true, .catch((err) => {
message: err, this.$message({
type: 'error' showClose: true,
message: err,
type: "error",
});
}); });
});
this.isImgLoading = false; this.isImgLoading = false;
} }
} }
}); });
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>