chore:新增保存文件到本地和读取本地文件功能

This commit is contained in:
zhaoxuhui 2018-11-05 17:57:47 +08:00
parent a036667426
commit 468559bf7c
30 changed files with 992 additions and 613 deletions

View File

@ -1,22 +1,14 @@
# vue-Markdown编辑器 # vue-Markdown编辑器
[在线示例地址](http://47.99.49.57/markdown/)
### 1.简介 ### 1.简介
**一款使用marked和highlight.js开发的一款markdown编辑器目前只支持在vue项目中使用。 **一款使用marked和highlight.js开发的一款markdown编辑器目前只支持在vue项目中使用。
编辑器涵盖了常用的markdown编辑器功能工具栏可自定义配置也可进行二次开发。** 编辑器涵盖了常用的markdown编辑器功能工具栏可自定义配置也可进行二次开发。**
**效果**
#### 提供的常用功能 ![image](http://smalleyes.oss-cn-shanghai.aliyuncs.com/WechatIMG586.png)
![image](https://noticejs.oss-cn-hangzhou.aliyuncs.com/gongneng.jpg)
#### 多种主题,分别支持 Light、DarkvsCode、OneDark、GitHub四种主题风格
![image](https://noticejs.oss-cn-hangzhou.aliyuncs.com/theme.jpg)
#### 一键打印
![image](https://noticejs.oss-cn-hangzhou.aliyuncs.com/print.jpg)
### 2.安装 ### 2.安装
@ -86,15 +78,15 @@ table |表格|是
checked|已完成列表|是 checked|已完成列表|是
notChecked |未完成列表|是 notChecked |未完成列表|是
shift|预览|是 shift|预览|是
print |打印| print |打印|
theme|主题切换|是 theme|主题切换|是
fullscreen |全屏|是 fullscreen |全屏|是
exportmd|导出为*.md文件|是
importmd|倒入本地*.md文件|是
### 7.其他说明 ### 7.其他说明
**关于保存时返回值** **关于保存时返回值**
``` ```
markdownValue // 编辑器输入的原始内容 markdownValue // 编辑器输入的原始内容
htmlValue // 右侧现实的问转义后的内容 htmlValue // 右侧现实的问转义后的内容
@ -141,3 +133,8 @@ v0.7.0
1.修复主题无法更新的问题 1.修复主题无法更新的问题
2.修复文档初始化值无法动态切换的问题 2.修复文档初始化值无法动态切换的问题
v0.8.0
1.新增md文件导出和读取功能
2.修改预览部分样式
3.修改头部菜单样式

7
dist/build.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/build.js.map vendored Normal file

File diff suppressed because one or more lines are too long

BIN
dist/checked.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
dist/iconfont.eot vendored

Binary file not shown.

27
dist/iconfont.svg vendored

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 49 KiB

BIN
dist/iconfont.ttf vendored

Binary file not shown.

2
dist/index.js vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

BIN
dist/notChecked.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,7 +1,7 @@
{ {
"name": "vue-meditor", "name": "vue-meditor",
"description": "一款使用marked和highlight.js开发的一款markdown编辑器", "description": "一款使用marked和highlight.js开发的一款markdown编辑器",
"version": "0.7.0", "version": "0.8.0",
"author": "zhaoxuhui<1258835133@qq.com>", "author": "zhaoxuhui<1258835133@qq.com>",
"license": "MIT", "license": "MIT",
"main": "dist/index.js", "main": "dist/index.js",

View File

@ -7,7 +7,7 @@
</template> </template>
<script> <script>
//import MarkDown from './markdown/index' // // import MarkDown from './markdown/index' //
import MarkDown from '../dist' // import MarkDown from '../dist' //
export default { export default {
name: 'app', name: 'app',

View File

@ -3,186 +3,229 @@
*Author zhaoxuhui *Author zhaoxuhui
*/ */
.markdown-preview { .markdown-preview {
max-width: 960px; max-width: 960px;
margin: 0 auto!important; margin: 0 auto !important;
ul { flex: 1;
list-style: none; overflow: hidden;
padding: 0 20px; overflow-y: scroll;
li {
position: relative;
&:after {
display: block;
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
position: absolute;
z-index: 99;
top: 7px;
left: -20px;
background: @content;
}
}
}
ol,
ul {
margin: 20px 0;
padding: 0 40px;
li {
font-size: 14px !important;
line-height: @line-height;
color: @title;
margin-bottom: 8px;
input[type="checkbox"] {
position: relative;
// transform: translateX(-40px);
&:after {
display: block;
content: "";
width: @line-height;
height: @line-height;
position: absolute;
z-index: 999;
background: #fff;
top: 0;
left: -30px;
}
}
}
}
hr {
color: @border;
height: 1px;
border: 0;
border-top: 1px solid @border;
margin: 20px 0;
padding: 0;
}
del,
em,
strong {
display: inline-block;
margin: @margin;
}
blockquote {
position: relative;
background: @background;
padding: 6px 12px;
border-left: 5px solid @divider;
border-radius: 2px;
margin: @margin;
}
/*基本样式*/
h1,
h2,
h3,
h4,
h5,
h6 {
color: @title;
}
h1 {
font-size: 28px;
border-bottom: 1px solid @border;
//text-align: center;
}
h2 {
font-size: 24px;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
font-size: 12px;
}
h1,
h2,
h3,
h4,
h5,
h6 {
/* border-bottom: 1px solid @border; */
padding: 8px 0;
}
p {
font-size: 14px !important;
color: @content;
margin: @margin;
line-height: @line-height;
}
img {
display: block;
max-width: 90%;
margin: 20px auto;
}
table {
width: 100%;
border: 1px solid @border;
border-bottom: 0;
background: #fff; background: #fff;
border-spacing: 0;
border-collapse: collapse;
margin: 20px 0;
tr {
-webkit-transition: background 0.1s;
transition: background 0.1s;
text-align: nav;
} >div{
tr td, padding: 10px 12px !important;
tr th { background: #fff;
padding: 0 8px; &::-webkit-scrollbar {
font-size: 14px; display: none;
line-height: 39px;
color: #333;
border-bottom: 1px solid @border;
cursor: default;
}
th {
background: #f8f8f9;
text-align: left;
font-weight: bold;
}
tr:nth-of-type(even) {
td {
background: #f8f8f9;
}
}
tr{
&:hover{
td{
background: #eaf5f6;
} }
}
} }
td, &::-webkit-scrollbar {
th { display: none;
border: 1px solid @border; }
ul {
list-style: none;
padding: 0 20px;
li {
position: relative;
&:after {
display: block;
content: "";
width: 8px;
height: 8px;
border-radius: 50%;
position: absolute;
z-index: 99;
top: 7px;
left: -20px;
background: @content;
}
}
}
ol,
ul {
margin: 20px 0;
padding: 0 40px;
li {
font-size: 14px !important;
color: @content;
margin-bottom: 10px;
line-height: 24px;
padding-left: 12px;
input[type="checkbox"] {
position: relative;
cursor: pointer;
overflow: visible;
position: absolute;
left: 0;
top: 0;
&:after {
display: block;
content: "";
width: 16px;
height: 16px;
position: absolute;
z-index:99999;
background: #fff;
top: 0;
right: 0;
}
&:before {
display: block;
width: 18px;
height: 18px;
position: absolute;
content: '';
top: 2px;
left: -25px;
z-index: 999999;
background: url("../img/notChecked.jpg") no-repeat;
background-size: contain;
}
}
input[type="checkbox"]:checked {
&:before {
background: url("../img/checked.jpg") no-repeat;
background-size: contain;
}
}
}
}
ol {
list-style-type: decimal;
}
hr {
color: @border;
height: 1px;
border: 0;
border-top: 1px solid @border;
margin: 20px 0;
padding: 0;
}
del,
em,
strong {
display: inline-block;
margin: @margin;
}
blockquote {
position: relative;
background: @background;
padding: 6px 12px;
border-left: 5px solid @divider;
border-radius: 2px;
margin: @margin;
}
/*基本样式*/
h1,
h2,
h3,
h4,
h5,
h6 {
color: @title;
}
h1 {
font-size: 28px;
border-bottom: 1px solid @border;
//text-align: center;
}
h2 {
font-size: 24px;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
font-size: 12px;
}
h1,
h2,
h3,
h4,
h5,
h6 {
/* border-bottom: 1px solid @border; */
padding: 8px 0;
font-weight: 600;
}
p {
font-size: 14px !important;
color: @content;
margin: @margin;
line-height: @line-height;
}
img {
display: block;
width: 90%;
margin: 20px auto;
}
table {
width: 100%;
border: 1px solid @border;
border-bottom: 0;
background: #fff;
border-spacing: 0;
border-collapse: collapse;
margin: 20px 0;
tr {
-webkit-transition: background 0.1s;
transition: background 0.1s;
text-align: nav;
}
tr td,
tr th {
padding: 0 8px;
font-size: 14px;
line-height: 39px;
color: #333;
border-bottom: 1px solid @border;
cursor: pointer;
}
th {
background: #f8f8f9;
text-align: left;
font-weight: bold;
}
tr:nth-of-type(even) {
td {
background: #f8f8f9;
}
}
tr {
&:hover {
td {
background: #eaf5f6;
}
}
}
td,
th {
border: 1px solid @border;
}
}
input[type="checkbox"] {
display: inline-block;
border-radius: 0;
margin-right: 8px;
}
a {
text-decoration: none;
color: @info;
font-size: 14px;
line-height: @line-height;
} }
}
input[type="checkbox"] {
display: inline-block;
border-radius: 0;
margin-right: 8px;
}
a {
text-decoration: none;
color: @primary;
font-size: 14px;
line-height: @line-height;
}
} }
@media only screen and (min-width: 1600px ) { @media only screen and (min-width: 1600px ) {
.markdown-preview { .markdown-preview {
max-width: 60%; max-width: 60%;
margin: 0 auto !important; margin: 0 auto !important;
} }
} }

View File

@ -13,7 +13,7 @@
background: #1e1e1e; background: #1e1e1e;
color: #DCDCDC; color: #DCDCDC;
overflow-y: hidden !important; overflow-y: hidden !important;
overflow-x: scroll !important; overflow-x: auto !important;
font-family: Menlo, Consolas, "Courier New", Courier, FreeMono, monospace !important; font-family: Menlo, Consolas, "Courier New", Courier, FreeMono, monospace !important;
* { * {
line-height: 1.6 !important; line-height: 1.6 !important;

View File

@ -14,7 +14,7 @@
line-height: 20px; line-height: 20px;
border-radius: 4px; border-radius: 4px;
margin: 20px 0 !important; margin: 20px 0 !important;
overflow-x: scroll !important; overflow-x: auto !important;
* { * {
font-family: Consolas !important; font-family: Consolas !important;

View File

@ -1,219 +1,501 @@
@margin: 8px 0;
@line-height: 22px;
.markdown { .markdown {
* { * {
margin: 0;
padding: 0;
box-sizing: border-box;
}
&.fullscreen {
position: fixed;
z-index: 999999;
top: 0;
left: 0;
right: 0;
bottom: 0;
.markdown-content {
padding: 0;
padding-top: 10px;
}
}
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
}
&.fullscreen {
position: fixed;
z-index: 999;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
margin: 0;
padding: 0;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
background: #eaeaea;
min-height: 400px;
.markdown-toolbars {
width: 100%;
display: flex;
align-items: center;
list-style: none;
background: #fff;
color: #464c5b;
height: 46px;
cursor: pointer;
box-shadow: 0 2px 3px #ddd;
padding-left: 4px;
border-bottom: 1px solid @border;
>li {
position: relative;
cursor: default;
&:after {
display: block;
content: attr(name);
position: absolute;
z-index: 999;
top: 34px;
left: 30%;
background: #e6e6e6;
color: #333;
white-space: nowrap;
font-size: 12px;
line-height: 20px;
padding: 0 6px;
border: 1px solid @border;
transition: all 0.3s;
transform: scale(0);
opacity: 0;
transform-origin: top;
}
&:hover {
&:after {
transform: scale(1);
opacity: 1;
}
}
&:last-child{
&:after{
right: 20%;
left: auto;
}
}
}
.empty {
flex: 1;
}
span {
padding: 0 8px;
transition: all 0.3s;
font-size: 14px;
display: inline-block;
line-height: 32px;
&:hover {
color: @primary;
background: @background;
border-radius: 3px;
}
}
.title {
padding-left: 4px;
padding-right: 10px;
}
li:last-child {
span {
font-size: 20px !important;
}
}
.shift-theme {
height: 46px;
//width: 80px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
span {
padding: 0 8px;
transition: all 0.3s;
font-size: 18px;
display: inline-block;
line-height: 32px;
&:hover {
color: @primary;
background: @background;
border-radius: 3px;
}
}
ul {
position: absolute;
z-index: 9999999;
top: 46px;
left: 50%;
margin-left: -41px;
background: #fff;
list-style: none;
font-size: 12px;
opacity: 0;
transition: all 0.3s;
transform-origin: top left;
transform: scaleY(0);
border: 1px solid @border;
border-top: 0;
&.active {
opacity: 1;
transform: scaleY(1);
}
li {
transition: all 0.3s;
padding: 0 15px;
width: 82px;
line-height: 30px;
border-bottom: 1px dashed @border;
&:last-child {
border-bottom: none;
}
&:hover {
background: @background;
color: @primary;
}
}
}
}
}
.markdown-content {
flex: 1;
width: 100%;
height: 100%;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
position: relative; align-items: center;
overflow: hidden; flex-direction: column;
padding-top: 12px; background: @background;
.markdown-editor { min-height: 400px;
flex: 1; .markdown-toolbars {
min-height: 100%;
position: relative;
margin: 0 !important;
overflow: hidden;
overflow-y: scroll;
display: flex;
justify-content: space-between;
&::-webkit-scrollbar {
display: none;
}
.index {
background: #272727;
min-height: 100%;
width: 36px;
line-height: @line-height;
padding: 12px 0;
li {
background: #272727;
color: #ccc;
font-size: 14px;
text-align: center;
font-family: Consolas;
}
}
textarea {
width: 100%; width: 100%;
min-height: 100%; display: flex;
outline: none; align-items: center;
border: 0; list-style: none;
background: #2d2d2d; background: #fff;
line-height: @line-height; color: #464c5b;
caret-color: #ccc; height: 40px;
color: #669acc; cursor: pointer;
font-size: 14px; //box-shadow: 0 2px 3px #ddd;
font-family: Consolas; padding-left: 4px;
resize: none; border-bottom: 1px solid @border;
padding: 12px 8px; > li {
overflow: hidden; position: relative;
&::selection { cursor: pointer;
background: #999; &:after {
color: @primary; display: block;
content: attr(name);
position: absolute;
z-index: 999999999999;
top: 32px;
left: 20px;
background: #e6e6e6;
color: #333;
white-space: nowrap;
font-size: 12px;
line-height: 20px;
padding: 0 6px;
border: 1px solid @border;
transition: all 0.3s 0.3s;
transform: scale(0);
opacity: 0;
transform-origin: top;
}
&:hover {
&:after {
transform: scale(1);
opacity: 1;
}
}
&:last-child {
&:after {
right: 20%;
left: auto;
}
}
.title{
font-size: 16px!important;
}
}
.empty {
flex: 1;
}
span {
font-size: 18px;
color: #999;
cursor: pointer;
display: block;
width: 30px;
height: 30px;
border-radius: 3px;
line-height: 30px;
text-align: center;
&:hover {
background: @background;
color: #0084ff;
}
}
.title {
padding-left: 4px;
padding-right: 10px;
}
li:last-child {
span {
font-size: 20px !important;
margin-right: 10px;
}
}
.shift-theme,.export-file {
height: 46px;
//width: 80px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
span {
padding: 0 8px;
transition: all 0.3s;
font-size: 18px;
display: inline-block;
line-height: 32px;
&:hover {
color: #0084ff;
background: @background;
border-radius: 3px;
}
}
ul {
position: absolute;
transform: scale(0);
transition: all 0.3s;
left: -50%;
top: 40px;
width: 160px;
transform-origin: top center;
list-style: none;
margin: 0;
padding:6px 0;
box-sizing: border-box;
border: 1px solid @border;
background: #fff;
border-radius:4px;
position: absolute;
z-index: 9999999;
box-shadow: 0 2px 8px 0 rgba(0,0,0,0.1);
font-family: "Monospaced Number", "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;
&.active {
opacity: 1;
transform: scaleY(1);
}
li {
line-height: 30px;
padding: 0 12px;
padding-left: 12px;
font-size: 13px;
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
color: @content;
.iconfont{
font-size: 14px;
display: block;
height: 30px;
width: 30px;
line-height: 30px;
overflow: hidden;
&:hover{
color: @content;
}
}
i{
font-size: 13px;
display: block;
font-style: normal;
flex: 1;
white-space: normal;
}
&:last-child {
border-bottom: 0;
.iconfont{
font-size: 14px!important;
margin: 0!important;
}
}
&:hover {
background: @background;
}
&.disabled {
cursor: not-allowed;
color: @disabled;
&:hover {
background: transparent;
}
}
}
}
}
.import-file{
position: relative;
overflow: hidden;
input{
position: absolute;
z-index: 9999;
left: 0;
top: 0;
display: block;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
}
} }
}
} }
.markdown-preview { .markdown-content {
min-height: 100%; flex: 1;
flex: 1; width: 100%;
padding: 20px 12px; height: 100%;
background: #fff; display: flex;
overflow: hidden; justify-content: space-between;
overflow-y: scroll; position: relative;
&::-webkit-scrollbar { overflow: hidden;
display: none; //padding: 10px;
} padding-bottom: 0;
.markdown-editor {
flex: 1;
min-height: 100%;
position: relative;
margin: 0 !important;
overflow: hidden;
overflow-y: scroll;
display: flex;
justify-content: space-between;
background: #2d2d2d;
&::-webkit-scrollbar {
display: none;
}
.index {
background: #272727;
min-height: 100%;
width: 36px;
line-height: @line-height;
padding: 12px 0;
li {
background: #272727;
color: #ccc;
font-size: 14px;
text-align: center;
font-family: Consolas;
}
}
textarea {
width: 100%;
min-height: 100%;
outline: none;
border: 0;
background: #2d2d2d;
line-height: @line-height;
caret-color: #ccc;
color: #669acc;
font-size: 14px;
font-family: Consolas;
resize: none;
padding: 12px 8px;
overflow: hidden;
&::selection {
background: #999;
color: #0084ff;
}
}
}
.markdown-preview {
min-height: 100%;
}
}
}
.insert-img-model {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 99999;
background: @mask;
padding-top: 12%;
transition: all 0.3s;
opacity: 0;
display: none;
.model-container {
background: #fff;
width: 480px;
margin: 0 auto;
border-radius: 4px;
transition: all 0.3s;
transform: scale(0);
transform-origin: center;
.model-head {
line-height: 32px;
padding: 0 12px;
background: @background;
border-radius: 4px 4px 0 0;
box-shadow: 0 1px 2px @border;
display: flex;
justify-content: space-between;
span:nth-of-type(2) {
font-size: 14px;
padding-left: 12px;
cursor: pointer;
&:hover {
color: @error;
}
}
}
.model-content {
padding: 20px 12px;
padding-top: 0;
min-height: 180px;
.insert-url {
padding: 42px 0;
display: flex;
justify-content: space-between;
align-items: center;
input {
display: block;
border: 1px solid #ccc;
font-size: 14px;
padding: 4px 8px;
line-height: 24px;
color: #333;
background: #fff;
border-radius: 4px;
writing-mode: horizontal-tb;
text-rendering: auto;
transition: box-shadow 2s;
flex: 1;
&:focus {
border-color: @info;
}
&::placeholder{
color: @tip;
}
}
a {
display: block;
background: @info;
color: #fff;
line-height: 32px;
height: 32px;
font-size: 13px;
padding: 0 12px;
border-radius: 3px;
margin-left: 20px;
border: 1px solid @border;
transition: all 0.3s;
&:hover {
background: @dark-info;
}
}
}
.insert-local {
height: 120px;
border: 1px dashed @border;
border-radius: 4px;
transition: all 0.3s;
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
cursor: pointer;
span {
font-size: 40px;
color: @border;
line-height: 50px;
transition: all 0.3s;
}
p {
font-size: 14px;
color: @content;
transition: all 0.3s;
}
&:hover {
border-color: @info;
span, p {
color: @info;
}
}
input {
display: block;
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
}
}
}
.model-foot {
display: flex;
justify-content: flex-end;
align-items: center;
padding: 10px 12px;
display: none;
a {
display: block;
background: @background;
color: @title;
line-height: 26px;
height: 26px;
font-size: 13px;
padding: 0 12px;
border-radius: 3px;
margin-left: 12px;
border: 1px solid @border;
transition: all 0.3s;
&:hover {
background: @divider;
}
&.ok {
background: @info;
color: #fff;
border-color: @info;
&:hover {
background: @dark-info;
}
}
}
}
}
&.active{
opacity: 1;
display: block;
.model-container{
transform: scale(1);
}
}
}
ul.shift {
padding: 6px 12px;
display: flex;
align-items: center;
span {
font-size: 12px;
cursor: pointer;
user-select: none;
&.iconfont {
font-size: 14px;
}
}
label {
font-size: 12px;
padding-right: 10px;
position: relative;
cursor: pointer;
user-select: none;
}
input[type='radio'],
label {
transition: all 0.6s ease;
box-sizing: border-box;
}
input[type="radio"] + label::before {
content: "\a0";
display: inline-block;
vertical-align: middle;
margin-right: 4px;
width: 8px;
height: 8px;
border-radius: 50%;
border: 1px solid @primary;
padding: 2px;
}
input[type="radio"]:checked + label::before {
background-color: @primary;
background-clip: content-box;
padding: 2px;
}
input[type="radio"] {
position: absolute;
clip: rect(0, 0, 0, 0);
}
input[type="radio"]:checked + label {
color: @primary;
} }
}
} }

View File

@ -15,7 +15,7 @@
padding: 20px !important; padding: 20px !important;
border-radius: 4px !important; border-radius: 4px !important;
overflow-y: hidden !important; overflow-y: hidden !important;
overflow-x: scroll !important; overflow-x: auto !important;
margin: 20px 0 !important; margin: 20px 0 !important;
code { code {
line-height: @line-height !important; line-height: @line-height !important;

View File

@ -11,7 +11,7 @@
background: #292c34; background: #292c34;
border-radius: 4px; border-radius: 4px;
overflow-y: hidden !important; overflow-y: hidden !important;
overflow-x: scroll !important; overflow-x: auto !important;
margin: 20px 0 !important; margin: 20px 0 !important;
* { * {
line-height: 1.6 !important; line-height: 1.6 !important;

View File

@ -2,20 +2,26 @@
/* /*
*Author zhaoxuhui *Author zhaoxuhui
*/ */
@primary: #1890ff;
@line-height: 22px; //主体颜色 //主体颜色
@primary: #2d8cf0; @primary: #292d35;
@lightPrimary: #5cadff; @light-primary: #323741;
@darkPrimary: #2b85e4; // @dark-primary: #1c1e24;
@info: #2d8cf0; //
@info: #007acc;
@success: #19be6b; @success: #19be6b;
@warning: #ff9900; @warning: #ff9900;
@error: #ed3f14; // @error: #ed3f14;
@title: #1c2438; //
@content: #333; @title: #252525;
@subColor: #80848f; @content: #555;
@sub-color: #80848f;
@disabled: #bbbec4; @disabled: #bbbec4;
@border: #dddee1; @border: #dddee1;
@divider: #e9eaec; @divider: #e9eaec;
@background: #f8f8f9; @background: #f7f7f7;
@margin: 8px 0; @tip:#c1c1c1;
@primary-rgba:rgba(49,204,102,0.2);
@mask:rgba(0, 0, 0, 0.3);
@blue-rgba:rgba(0,122,204,0.2);
@dark-info:#0169af;

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,11 +1,5 @@
<template lang="html"> <template lang="html">
<div <div :class="isFullscreen?'markdown fullscreen':'markdown' " ref="markdown" :style="{height:`${editorHeight}px`}" @mouseover="addListener" @mouseout="removeListener">
:class="isFullscreen?'markdown fullscreen':'markdown' "
ref="markdown"
:style="{height:`${editorHeight}px`}"
@mouseover="addListener"
@mouseout="removeListener"
>
<!-- 头部工具栏 --> <!-- 头部工具栏 -->
<ul class="markdown-toolbars"> <ul class="markdown-toolbars">
<li class="title" v-if="title" :style="{titleStyle}">{{title}}</li> <li class="title" v-if="title" :style="{titleStyle}">{{title}}</li>
@ -18,23 +12,23 @@
<li v-if="tools.overline" name="删除线"> <li v-if="tools.overline" name="删除线">
<span @click="insertOverline" class="iconfont icon-overline"></span> <span @click="insertOverline" class="iconfont icon-overline"></span>
</li> </li>
<li v-if="tools.h1" name="标题1"> <li v-if="tools.h1" name="标题1" >
<span @click="insertTitle(1)">h1</span> <span @click="insertTitle(1)"class="title">h1</span>
</li> </li>
<li v-if="tools.h2" name="标题2"> <li v-if="tools.h2" name="标题2">
<span @click="insertTitle(2)">h2</span> <span @click="insertTitle(2)"class="title">h2</span>
</li> </li>
<li v-if="tools.h3" name="标题3"> <li v-if="tools.h3" name="标题3" >
<span @click="insertTitle(3)">h3</span> <span @click="insertTitle(3)"class="title">h3</span>
</li> </li>
<li v-if="tools.h4" name="标题4"> <li v-if="tools.h4" name="标题4" >
<span @click="insertTitle(4)">h4</span> <span @click="insertTitle(4)"class="title">h4</span>
</li> </li>
<li v-if="tools.h5" name="标题5"> <li v-if="tools.h5" name="标题5" >
<span @click="insertTitle(5)">h5</span> <span @click="insertTitle(5)"class="title">h5</span>
</li> </li>
<li v-if="tools.h6" name="标题6"> <li v-if="tools.h6" name="标题6">
<span @click="insertTitle(6)">h6</span> <span @click="insertTitle(6)"class="title">h6</span>
</li> </li>
<li v-if="tools.hr" name="分割线"> <li v-if="tools.hr" name="分割线">
<span @click="insertLine" class="iconfont icon-horizontal"></span> <span @click="insertLine" class="iconfont icon-horizontal"></span>
@ -64,24 +58,29 @@
<span @click="insertImage" class="iconfont icon-img"></span> <span @click="insertImage" class="iconfont icon-img"></span>
</li> </li>
<li v-if="tools.table" name="表格"> <li v-if="tools.table" name="表格">
<span <span @click="insertTable" class="iconfont icon-table"></span>
@click="insertTable"
class="iconfont icon-table"></span>
</li> </li>
<li v-if="tools.print" name="打印"> <li v-if="tools.print" name="打印">
<span class="iconfont icon-dayin" @click="print"></span> <span class="iconfont icon-dayin" @click="print"></span>
</li> </li>
<li v-if="tools.theme" class="shift-theme" name="代码块主题"> <li v-if="tools.theme" class="shift-theme" name="代码块主题">
<div> <div>
<span class="iconfont icon-theme" @click="toggleSlideDown"></span> <span class="iconfont icon-yanse" @click="toggleSlideDown"></span>
<ul :class="{active:slideDown}"> <ul :class="{active:slideDown}" @mouseleave="slideDown=false">
<li @click="setThemes('Light')">Light</li> <li @click="setThemes('Light')"> <span class="iconfont icon-theme"></span><i>Light</i></li>
<li @click="setThemes('Dark')">Dark</li> <li @click="setThemes('Dark')"><span class="iconfont icon-vip"></span><i>VS Code</i></li>
<li @click="setThemes('OneDark')">OneDark</li> <li @click="setThemes('OneDark')"><span class="iconfont icon-atom"></span><i>Atom OneDark</i></li>
<li @click="setThemes('GitHub')">GitHub</li> <li @click="setThemes('GitHub')"><span class="iconfont icon-github51"></span><i>GitHub</i></li>
</ul> </ul>
</div> </div>
</li> </li>
<li name="导入本地文件" class="import-file" v-show="tools.importmd">
<span class="iconfont icon-daoru"></span>
<input type="file" @change="importFile($event)">
</li>
<li name="保存到本地" v-show="tools.exportmd">
<span class="iconfont icon-download" @click="exportMd"></span>
</li>
<li v-if="tools.shift&&preview==1" name="预览"> <li v-if="tools.shift&&preview==1" name="预览">
<span @click="preview=2" class="iconfont icon-preview"></span> <span @click="preview=2" class="iconfont icon-preview"></span>
</li> </li>
@ -102,28 +101,14 @@
</ul> </ul>
<!-- 编辑器 --> <!-- 编辑器 -->
<div class="markdown-content"> <div class="markdown-content">
<div v-show="preview===1||preview===3" class="markdown-editor" ref="markdownContent" @scroll="markdownScroll" <div v-show="preview===1||preview===3" class="markdown-editor" ref="markdownContent" @scroll="markdownScroll" @mouseenter="mousescrollSide('markdown')">
@mouseenter="mousescrollSide('markdown')">
<ul class="index" ref="index" :style="{height:scrollHeight?`${scrollHeight}px`:'100%'}"> <ul class="index" ref="index" :style="{height:scrollHeight?`${scrollHeight}px`:'100%'}">
<li v-for="(item,index) in indexLenth">{{index+1}}</li> <li v-for="(item,index) in indexLenth">{{index+1}}</li>
</ul> </ul>
<textarea <textarea v-model="value" @keydown.tab="tab" @keyup.enter="enter" @keyup.delete="onDelete" ref="textarea" :style="{height:scrollHeight?`${scrollHeight}px`:'100%'}"></textarea>
v-model="value"
@keydown.tab="tab"
@keyup.enter="enter"
@keyup.delete="onDelete"
ref="textarea"
:style="{height:scrollHeight?`${scrollHeight}px`:'100%'}"
></textarea>
</div> </div>
<div v-show="preview==1" class="empty" style="width:12px;"></div> <div v-show="preview==1" class="empty" style="width:12px;"></div>
<div <div v-show="preview===1||preview===2" :class="`markdown-preview ${themeName}`" v-html="previewMarkdown" ref="preview" @scroll="previewScroll" @mouseenter="mousescrollSide('preview')">
v-show="preview===1||preview===2"
:class="`markdown-preview ${themeName}`"
v-html="previewMarkdown"
ref="preview"
@scroll="previewScroll"
@mouseenter="mousescrollSide('preview')">
</div> </div>
</div> </div>
</div> </div>
@ -133,6 +118,7 @@
import markdown from './markdown'; import markdown from './markdown';
export default markdown; export default markdown;
</script> </script>
<style lang="less"> <style lang="less">
@ -143,5 +129,6 @@
@import "css/gitHub"; @import "css/gitHub";
@import "css/common"; @import "css/common";
@import "css/index"; @import "css/index";
@import "font/iconfont.css"; @import "./font/iconfont.css";
</style> </style>

View File

@ -6,110 +6,112 @@ import Print from './js/print';
hljs.initHighlightingOnLoad(); hljs.initHighlightingOnLoad();
marked.setOptions({ marked.setOptions({
renderer: new marked.Renderer(), renderer : new marked.Renderer(),
gfm: true, gfm : true,
tables: true, tables : true,
breaks: false, breaks : false,
pedantic: false, pedantic : false,
sanitize: true, sanitize : true,
smartLists: true, smartLists : true,
highlight: function (code) { highlight : function (code) {
return hljs.highlightAuto(code).value; return hljs.highlightAuto(code).value;
} }
}); });
export default { export default {
name: 'markdown', name : 'markdown',
props: { props : {
title: { // 标题 title : { // 标题
type: String, type : String,
default: '' default : ''
}, },
titleStyle: { // 标题样式 titleStyle : {// 标题样式
type: Object, type : Object,
default () { default() {
return {} return {}
} }
}, },
theme: { // 默认主题 theme : { // 默认主题
type: String, type : String,
default: 'Light' default : 'Light'
}, },
width: { // 宽度 width : {// 宽度
type: [Number, String], type : [Number, String],
default: 'auto' default : 'auto'
}, },
height: { // 高度 height : { // 高度
type: Number, type : Number,
default: 600 default : 600
}, // 宽度 }, // 宽度
toolbars: { // 工具栏 toolbars : { // 工具栏
type: Object, type : Object,
default () { default() {
return {}; return {};
} }
}, },
autoSave: { // 是否自动保存 autoSave : {// 是否自动保存
type: Boolean, type : Boolean,
default: true default : true
}, },
interval: { // 自动保存频率 单位:毫秒 interval : { // 自动保存频率 单位:毫秒
type: Number, type : Number,
default: 10000 default : 10000
}, },
initialValue: { // 初始化值 initialValue : { // 初始化值
type: String, type : String,
default: '' default : ''
}, },
mode: { // 模式 1 分屏显示 2 预览详情 3 全屏编辑 mode : { // 模式 1 分屏显示 2 预览详情 3 全屏编辑
type: [Number, String], type : [Number, String],
default: 1 default : 1
} }
}, },
data() { data() {
return { return {
value: '', // 输入框内容 value : '', // 输入框内容
timeoutId: null, timeoutId : null,
hljsInit: null, hljsInit : null,
indexLenth: 1, indexLenth : 1,
previewMarkdown: '', previewMarkdown : '',
preview: 1, // 是否是预览状态 preview : 1, // 是否是预览状态
isFullscreen: false, isFullscreen : false,
scrollHeight: null, scrollHeight : null,
scroll: 'markdown', // 哪个半栏在滑动 scroll : 'markdown',// 哪个半栏在滑动
allTools: { // 显示隐藏的工具栏 allTools : { // 显示隐藏的工具栏
strong: true, strong : true,
italic: true, italic : true,
overline: true, overline : true,
h1: true, h1 : true,
h2: true, h2 : true,
h3: true, h3 : true,
h4: false, h4 : false,
h5: false, h5 : false,
h6: false, h6 : false,
hr: true, hr : true,
quote: true, quote : true,
ul: true, ul : true,
ol: true, ol : true,
code: true, code : true,
link: true, link : true,
image: true, image : true,
table: true, table : true,
checked: true, checked : true,
notChecked: true, notChecked : true,
shift: true, shift : true,
fullscreen: true, fullscreen : true,
print: true, print : false,
theme: true theme: true,
exportmd:true,
importmd:true
}, },
slideDown: false, slideDown : false,
themeName: 'Light', // 主题名称 themeName : 'Light',// 主题名称
lastInsert: '', lastInsert : '',
timerId: null, // 定时器id timerId : null,// 定时器id
}; };
}, },
computed: { computed : {
editorHeight() { editorHeight() {
if (this.isFullscreen) { if (this.isFullscreen) {
return window.innerHeight; return window.innerHeight;
@ -118,34 +120,26 @@ export default {
} }
}, },
tools() { tools() {
const { const {allTools, toolbars} = this;
allTools,
toolbars
} = this;
return Object.assign(allTools, toolbars) return Object.assign(allTools, toolbars)
} }
}, },
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.textarea.focus(); this.$refs.textarea.focus();
}) })
this.init(); this.init();
//this.addListener(); //this.addListener();
}, },
methods: { methods : {
init() { init() {
this.themeName = this.theme; this.themeName = this.theme;
const { const {autoSave, interval, theme, initialValue, mode} = this;
autoSave,
interval,
theme,
initialValue,
mode
} = this;
this.value = initialValue; this.value = initialValue;
this.preview = mode; this.preview = mode;
this.previewMarkdown = marked(initialValue, { this.previewMarkdown = marked(initialValue, {
sanitize: true sanitize : true
}); });
if (autoSave) { if (autoSave) {
this.timerId = setInterval(() => { this.timerId = setInterval(() => {
@ -174,13 +168,11 @@ export default {
markdownContent.scrollTop = parseInt((previewScrollTop / previewScrollHeight) * markdownScrollHeight); markdownContent.scrollTop = parseInt((previewScrollTop / previewScrollHeight) * markdownScrollHeight);
} }
}, },
mousescrollSide(side) { // 设置究竟是哪个半边在主动滑动 mousescrollSide(side) {// 设置究竟是哪个半边在主动滑动
this.scroll = side; this.scroll = side;
}, },
insertContent(str) { // 插入文本 insertContent(str) { // 插入文本
const { const {preview} = this;
preview
} = this;
if (preview === 2) { if (preview === 2) {
return; return;
} }
@ -202,7 +194,7 @@ export default {
if (document.selection) { if (document.selection) {
textDom.focus(); textDom.focus();
let selectRange = document.selection.createRange(); let selectRange = document.selection.createRange();
selectRange.moveStart('character', -this.value.length); selectRange.moveStart('character', - this.value.length);
cursorPos = selectRange.text.length; cursorPos = selectRange.text.length;
} else if (textDom.selectionStart || textDom.selectionStart == '0') { } else if (textDom.selectionStart || textDom.selectionStart == '0') {
cursorPos = textDom.selectionStart; cursorPos = textDom.selectionStart;
@ -250,12 +242,12 @@ export default {
}, },
insertTitle(level) { // 插入标题 insertTitle(level) { // 插入标题
const titleLevel = { const titleLevel = {
1: '\n# ', 1 : '\n# ',
2: '\n## ', 2 : '\n## ',
3: '\n### ', 3 : '\n### ',
4: '\n#### ', 4 : '\n#### ',
5: '\n##### ', 5 : '\n##### ',
6: '\n###### ' 6 : '\n###### '
}; };
this.insertContent(titleLevel[level]); this.insertContent(titleLevel[level]);
}, },
@ -351,12 +343,12 @@ export default {
}, },
insertTitle(level) { // 插入标题 insertTitle(level) { // 插入标题
const titleLevel = { const titleLevel = {
1: '# ', 1 : '# ',
2: '## ', 2 : '## ',
3: '### ', 3 : '### ',
4: '#### ', 4 : '#### ',
5: '##### ', 5 : '##### ',
6: '###### ' 6 : '###### '
}; };
this.insertContent(titleLevel[level]); this.insertContent(titleLevel[level]);
}, },
@ -374,25 +366,23 @@ export default {
}, },
handleSave() { // 保存操作 handleSave() { // 保存操作
this.$emit('on-save', { this.$emit('on-save', {
markdownValue: this.value, markdownValue : this.value,
htmlValue: this.previewMarkdown, htmlValue : this.previewMarkdown,
theme: this.themeName theme : this.theme
}); });
}, },
insertLine() { // 插入分割线 insertLine() { // 插入分割线
this.insertContent(`\n----\n`); this.insertContent(`\n----\n`);
}, },
toggleSlideDown() { // 显示主题选项 toggleSlideDown() { // 显示主题选项
this.slideDown = !this.slideDown; this.slideDown = ! this.slideDown;
}, },
setThemes(name) { // 设置主题 setThemes(name) { // 设置主题
this.themeName = name; this.themeName = name;
this.slideDown = false; this.slideDown = false;
}, },
enter(e) { // 回车事件 enter(e) { // 回车事件
const { const {lastInsert} = this;
lastInsert
} = this;
const list = ['- ', '1. ', '- [ ] ', '- [x] '] const list = ['- ', '1. ', '- [ ] ', '- [x] ']
if (list.includes(lastInsert)) { if (list.includes(lastInsert)) {
this.insertContent(lastInsert); this.insertContent(lastInsert);
@ -410,26 +400,57 @@ export default {
}, },
addListener() { // 事件监听,阻止保存 addListener() { // 事件监听,阻止保存
this.removeListener(); this.removeListener();
document.addEventListener('keydown', this.listener) document.addEventListener('keydown',this.listener)
}, },
removeListener() { removeListener(){
document.removeEventListener('keydown', this.listener) document.removeEventListener('keydown',this.listener)
}, },
listener(e) { listener(e){
if (e.keyCode === 83) { if (e.keyCode === 83) {
if (e.metaKey || e.ctrlKey) { if( e.metaKey||e.ctrlKey){
e.preventDefault(); e.preventDefault();
this.handleSave(); this.handleSave();
} }
} }
},
importFile(e) { // 导入文件
const file = e.target.files[0];
if (! file) {
return;
}
const {type} = file;
if (type !== 'text/markdown') {
this.$Notice.error('文件格式有误!');
return;
}
const reader = new FileReader();
reader.readAsText(file, {encoding : 'utf-8'});
reader.onload = () => {
this.value = reader.result;
e.target.value = '';
}
},
exportMd() { // 导出文件到本地
const { value } = this;
var pom = document.createElement('a');
pom.setAttribute('href', 'data:text/plain;charset=UTF-8,' + encodeURIComponent(value));
pom.setAttribute('download', '未命名.md');
pom.style.display = 'none';
if (document.createEvent) {
var event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
pom.dispatchEvent(event);
} else {
pom.click();
}
} }
}, },
watch: { watch : {
value() { value() {
clearTimeout(this.timeoutId); clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => { this.timeoutId = setTimeout(() => {
this.previewMarkdown = marked(this.value, { this.previewMarkdown = marked(this.value, {
sanitize: true sanitize : true
}); });
}, 30) }, 30)
this.indexLenth = this.value.split('\n').length; this.indexLenth = this.value.split('\n').length;
@ -437,12 +458,6 @@ export default {
const height_2 = this.$refs.textarea.scrollHeight; const height_2 = this.$refs.textarea.scrollHeight;
const height_3 = this.$refs.preview.scrollHeight; const height_3 = this.$refs.preview.scrollHeight;
this.scrollHeight = Math.max(height_1, height_2, height_3); this.scrollHeight = Math.max(height_1, height_2, height_3);
},
initialValue() {
this.value = this.initialValue;
},
theme() {
this.themeName = this.theme;
} }
}, },
destroyed() { // 销毁时清除定时器 destroyed() { // 销毁时清除定时器

View File

@ -2,11 +2,11 @@ var path = require('path')
var webpack = require('webpack') var webpack = require('webpack')
module.exports = { module.exports = {
entry: './src/main.js', entry: './src/main.js', // 打包为npm包时将此处修改为 ./src/index.js
output: { output: {
path: path.resolve(__dirname, './dist'), path: path.resolve(__dirname, './dist'),
publicPath: '/dist/', publicPath: '/dist/',
filename: 'build.js', filename: 'build.js',// 打包为npm包时将此处修改为 index.js
libraryTarget: 'umd', libraryTarget: 'umd',
library: 'markdown-vue', library: 'markdown-vue',
umdNamedDefine: true umdNamedDefine: true