chore:新增保存文件到本地和读取本地文件功能
29
README.md
@ -1,22 +1,14 @@
|
||||
# vue-Markdown编辑器
|
||||
|
||||
[在线示例地址](http://47.99.49.57/markdown/)
|
||||
|
||||
### 1.简介
|
||||
|
||||
**一款使用marked和highlight.js开发的一款markdown编辑器,目前只支持在vue项目中使用。
|
||||
编辑器涵盖了常用的markdown编辑器功能,工具栏可自定义配置,也可进行二次开发。**
|
||||
|
||||
|
||||
#### 提供的常用功能
|
||||
![image](https://noticejs.oss-cn-hangzhou.aliyuncs.com/gongneng.jpg)
|
||||
|
||||
#### 多种主题,分别支持 Light、Dark(vsCode)、OneDark、GitHub四种主题风格
|
||||
|
||||
![image](https://noticejs.oss-cn-hangzhou.aliyuncs.com/theme.jpg)
|
||||
|
||||
#### 一键打印
|
||||
|
||||
![image](https://noticejs.oss-cn-hangzhou.aliyuncs.com/print.jpg)
|
||||
|
||||
|
||||
**效果**
|
||||
![image](http://smalleyes.oss-cn-shanghai.aliyuncs.com/WechatIMG586.png)
|
||||
|
||||
### 2.安装
|
||||
|
||||
@ -86,15 +78,15 @@ table |表格|是
|
||||
checked|已完成列表|是
|
||||
notChecked |未完成列表|是
|
||||
shift|预览|是
|
||||
print |打印|是
|
||||
print |打印|否
|
||||
theme|主题切换|是
|
||||
fullscreen |全屏|是
|
||||
|
||||
exportmd|导出为*.md文件|是
|
||||
importmd|倒入本地*.md文件|是
|
||||
|
||||
### 7.其他说明
|
||||
**关于保存时返回值**
|
||||
|
||||
|
||||
```
|
||||
markdownValue // 编辑器输入的原始内容
|
||||
htmlValue // 右侧现实的问转义后的内容
|
||||
@ -141,3 +133,8 @@ v0.7.0
|
||||
|
||||
1.修复主题无法更新的问题
|
||||
2.修复文档初始化值无法动态切换的问题
|
||||
|
||||
v0.8.0
|
||||
1.新增md文件导出和读取功能
|
||||
2.修改预览部分样式
|
||||
3.修改头部菜单样式
|
||||
|
7
dist/build.js
vendored
Normal file
1
dist/build.js.map
vendored
Normal file
BIN
dist/checked.jpg
vendored
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
dist/iconfont.eot
vendored
27
dist/iconfont.svg
vendored
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 49 KiB |
BIN
dist/iconfont.ttf
vendored
2
dist/index.js
vendored
2
dist/index.js.map
vendored
BIN
dist/notChecked.jpg
vendored
Normal file
After Width: | Height: | Size: 16 KiB |
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vue-meditor",
|
||||
"description": "一款使用marked和highlight.js开发的一款markdown编辑器",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.0",
|
||||
"author": "zhaoxuhui<1258835133@qq.com>",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
|
@ -7,7 +7,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
//import MarkDown from './markdown/index' // 开发文件
|
||||
// import MarkDown from './markdown/index' // 开发文件
|
||||
import MarkDown from '../dist' // 引入打包好的文件
|
||||
export default {
|
||||
name: 'app',
|
||||
|
@ -4,7 +4,22 @@
|
||||
*/
|
||||
.markdown-preview {
|
||||
max-width: 960px;
|
||||
margin: 0 auto!important;
|
||||
margin: 0 auto !important;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
background: #fff;
|
||||
|
||||
>div{
|
||||
padding: 10px 12px !important;
|
||||
background: #fff;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0 20px;
|
||||
@ -13,8 +28,8 @@
|
||||
&:after {
|
||||
display: block;
|
||||
content: "";
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
@ -29,28 +44,55 @@
|
||||
ul {
|
||||
margin: 20px 0;
|
||||
padding: 0 40px;
|
||||
|
||||
li {
|
||||
font-size: 14px !important;
|
||||
line-height: @line-height;
|
||||
color: @title;
|
||||
margin-bottom: 8px;
|
||||
color: @content;
|
||||
margin-bottom: 10px;
|
||||
line-height: 24px;
|
||||
padding-left: 12px;
|
||||
input[type="checkbox"] {
|
||||
position: relative;
|
||||
// transform: translateX(-40px);
|
||||
cursor: pointer;
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
&:after {
|
||||
display: block;
|
||||
content: "";
|
||||
width: @line-height;
|
||||
height: @line-height;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
z-index:99999;
|
||||
background: #fff;
|
||||
top: 0;
|
||||
left: -30px;
|
||||
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;
|
||||
@ -110,6 +152,7 @@
|
||||
h6 {
|
||||
/* border-bottom: 1px solid @border; */
|
||||
padding: 8px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
p {
|
||||
font-size: 14px !important;
|
||||
@ -119,7 +162,7 @@
|
||||
}
|
||||
img {
|
||||
display: block;
|
||||
max-width: 90%;
|
||||
width: 90%;
|
||||
margin: 20px auto;
|
||||
}
|
||||
table {
|
||||
@ -143,7 +186,7 @@
|
||||
line-height: 39px;
|
||||
color: #333;
|
||||
border-bottom: 1px solid @border;
|
||||
cursor: default;
|
||||
cursor: pointer;
|
||||
}
|
||||
th {
|
||||
background: #f8f8f9;
|
||||
@ -155,9 +198,9 @@
|
||||
background: #f8f8f9;
|
||||
}
|
||||
}
|
||||
tr{
|
||||
&:hover{
|
||||
td{
|
||||
tr {
|
||||
&:hover {
|
||||
td {
|
||||
background: #eaf5f6;
|
||||
}
|
||||
}
|
||||
@ -174,7 +217,7 @@
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: @primary;
|
||||
color: @info;
|
||||
font-size: 14px;
|
||||
line-height: @line-height;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
background: #1e1e1e;
|
||||
color: #DCDCDC;
|
||||
overflow-y: hidden !important;
|
||||
overflow-x: scroll !important;
|
||||
overflow-x: auto !important;
|
||||
font-family: Menlo, Consolas, "Courier New", Courier, FreeMono, monospace !important;
|
||||
* {
|
||||
line-height: 1.6 !important;
|
||||
|
@ -14,7 +14,7 @@
|
||||
line-height: 20px;
|
||||
border-radius: 4px;
|
||||
margin: 20px 0 !important;
|
||||
overflow-x: scroll !important;
|
||||
overflow-x: auto !important;
|
||||
|
||||
* {
|
||||
font-family: Consolas !important;
|
||||
|
@ -1,4 +1,5 @@
|
||||
|
||||
@margin: 8px 0;
|
||||
@line-height: 22px;
|
||||
.markdown {
|
||||
* {
|
||||
margin: 0;
|
||||
@ -7,11 +8,15 @@
|
||||
}
|
||||
&.fullscreen {
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
z-index: 999999;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
.markdown-content {
|
||||
padding: 0;
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@ -20,7 +25,7 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
background: #eaeaea;
|
||||
background: @background;
|
||||
min-height: 400px;
|
||||
.markdown-toolbars {
|
||||
width: 100%;
|
||||
@ -29,21 +34,21 @@
|
||||
list-style: none;
|
||||
background: #fff;
|
||||
color: #464c5b;
|
||||
height: 46px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 3px #ddd;
|
||||
//box-shadow: 0 2px 3px #ddd;
|
||||
padding-left: 4px;
|
||||
border-bottom: 1px solid @border;
|
||||
>li {
|
||||
> li {
|
||||
position: relative;
|
||||
cursor: default;
|
||||
cursor: pointer;
|
||||
&:after {
|
||||
display: block;
|
||||
content: attr(name);
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
top: 34px;
|
||||
left: 30%;
|
||||
z-index: 999999999999;
|
||||
top: 32px;
|
||||
left: 20px;
|
||||
background: #e6e6e6;
|
||||
color: #333;
|
||||
white-space: nowrap;
|
||||
@ -51,7 +56,7 @@
|
||||
line-height: 20px;
|
||||
padding: 0 6px;
|
||||
border: 1px solid @border;
|
||||
transition: all 0.3s;
|
||||
transition: all 0.3s 0.3s;
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
transform-origin: top;
|
||||
@ -62,26 +67,32 @@
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&:last-child{
|
||||
&:after{
|
||||
&:last-child {
|
||||
&:after {
|
||||
right: 20%;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
.title{
|
||||
font-size: 16px!important;
|
||||
}
|
||||
}
|
||||
.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;
|
||||
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 {
|
||||
@ -91,9 +102,10 @@
|
||||
li:last-child {
|
||||
span {
|
||||
font-size: 20px !important;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.shift-theme {
|
||||
.shift-theme,.export-file {
|
||||
|
||||
height: 46px;
|
||||
//width: 80px;
|
||||
@ -108,47 +120,100 @@
|
||||
display: inline-block;
|
||||
line-height: 32px;
|
||||
&:hover {
|
||||
color: @primary;
|
||||
color: #0084ff;
|
||||
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;
|
||||
transform: scale(0);
|
||||
transition: all 0.3s;
|
||||
transform-origin: top left;
|
||||
transform: scaleY(0);
|
||||
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;
|
||||
border-top: 0;
|
||||
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 {
|
||||
transition: all 0.3s;
|
||||
padding: 0 15px;
|
||||
width: 82px;
|
||||
line-height: 30px;
|
||||
border-bottom: 1px dashed @border;
|
||||
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: none;
|
||||
border-bottom: 0;
|
||||
.iconfont{
|
||||
font-size: 14px!important;
|
||||
margin: 0!important;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background: @background;
|
||||
color: @primary;
|
||||
}
|
||||
&.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-content {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
@ -157,7 +222,8 @@
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding-top: 12px;
|
||||
//padding: 10px;
|
||||
padding-bottom: 0;
|
||||
.markdown-editor {
|
||||
flex: 1;
|
||||
min-height: 100%;
|
||||
@ -167,6 +233,7 @@
|
||||
overflow-y: scroll;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: #2d2d2d;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
@ -200,20 +267,235 @@
|
||||
overflow: hidden;
|
||||
&::selection {
|
||||
background: #999;
|
||||
color: @primary;
|
||||
color: #0084ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.markdown-preview {
|
||||
min-height: 100%;
|
||||
flex: 1;
|
||||
padding: 20px 12px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
padding: 20px !important;
|
||||
border-radius: 4px !important;
|
||||
overflow-y: hidden !important;
|
||||
overflow-x: scroll !important;
|
||||
overflow-x: auto !important;
|
||||
margin: 20px 0 !important;
|
||||
code {
|
||||
line-height: @line-height !important;
|
||||
|
@ -11,7 +11,7 @@
|
||||
background: #292c34;
|
||||
border-radius: 4px;
|
||||
overflow-y: hidden !important;
|
||||
overflow-x: scroll !important;
|
||||
overflow-x: auto !important;
|
||||
margin: 20px 0 !important;
|
||||
* {
|
||||
line-height: 1.6 !important;
|
||||
|
@ -2,20 +2,26 @@
|
||||
/*
|
||||
*Author zhaoxuhui
|
||||
*/
|
||||
@primary: #1890ff;
|
||||
@line-height: 22px; //主体颜色
|
||||
@primary: #2d8cf0;
|
||||
@lightPrimary: #5cadff;
|
||||
@darkPrimary: #2b85e4; //
|
||||
@info: #2d8cf0;
|
||||
|
||||
//主体颜色
|
||||
@primary: #292d35;
|
||||
@light-primary: #323741;
|
||||
@dark-primary: #1c1e24;
|
||||
//
|
||||
@info: #007acc;
|
||||
@success: #19be6b;
|
||||
@warning: #ff9900;
|
||||
@error: #ed3f14; //
|
||||
@title: #1c2438;
|
||||
@content: #333;
|
||||
@subColor: #80848f;
|
||||
@error: #ed3f14;
|
||||
//
|
||||
@title: #252525;
|
||||
@content: #555;
|
||||
@sub-color: #80848f;
|
||||
@disabled: #bbbec4;
|
||||
@border: #dddee1;
|
||||
@divider: #e9eaec;
|
||||
@background: #f8f8f9;
|
||||
@margin: 8px 0;
|
||||
@background: #f7f7f7;
|
||||
@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;
|
||||
|
1
src/markdown/font/iconfont.js
Normal file
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 49 KiB |
BIN
src/markdown/img/checked.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
src/markdown/img/notChecked.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
@ -1,11 +1,5 @@
|
||||
<template lang="html">
|
||||
<div
|
||||
:class="isFullscreen?'markdown fullscreen':'markdown' "
|
||||
ref="markdown"
|
||||
:style="{height:`${editorHeight}px`}"
|
||||
@mouseover="addListener"
|
||||
@mouseout="removeListener"
|
||||
>
|
||||
<div :class="isFullscreen?'markdown fullscreen':'markdown' " ref="markdown" :style="{height:`${editorHeight}px`}" @mouseover="addListener" @mouseout="removeListener">
|
||||
<!-- 头部工具栏 -->
|
||||
<ul class="markdown-toolbars">
|
||||
<li class="title" v-if="title" :style="{titleStyle}">{{title}}</li>
|
||||
@ -18,23 +12,23 @@
|
||||
<li v-if="tools.overline" name="删除线">
|
||||
<span @click="insertOverline" class="iconfont icon-overline"></span>
|
||||
</li>
|
||||
<li v-if="tools.h1" name="标题1">
|
||||
<span @click="insertTitle(1)">h1</span>
|
||||
<li v-if="tools.h1" name="标题1" >
|
||||
<span @click="insertTitle(1)"class="title">h1</span>
|
||||
</li>
|
||||
<li v-if="tools.h2" name="标题2">
|
||||
<span @click="insertTitle(2)">h2</span>
|
||||
<span @click="insertTitle(2)"class="title">h2</span>
|
||||
</li>
|
||||
<li v-if="tools.h3" name="标题3">
|
||||
<span @click="insertTitle(3)">h3</span>
|
||||
<li v-if="tools.h3" name="标题3" >
|
||||
<span @click="insertTitle(3)"class="title">h3</span>
|
||||
</li>
|
||||
<li v-if="tools.h4" name="标题4">
|
||||
<span @click="insertTitle(4)">h4</span>
|
||||
<li v-if="tools.h4" name="标题4" >
|
||||
<span @click="insertTitle(4)"class="title">h4</span>
|
||||
</li>
|
||||
<li v-if="tools.h5" name="标题5">
|
||||
<span @click="insertTitle(5)">h5</span>
|
||||
<li v-if="tools.h5" name="标题5" >
|
||||
<span @click="insertTitle(5)"class="title">h5</span>
|
||||
</li>
|
||||
<li v-if="tools.h6" name="标题6">
|
||||
<span @click="insertTitle(6)">h6</span>
|
||||
<span @click="insertTitle(6)"class="title">h6</span>
|
||||
</li>
|
||||
<li v-if="tools.hr" name="分割线">
|
||||
<span @click="insertLine" class="iconfont icon-horizontal"></span>
|
||||
@ -64,24 +58,29 @@
|
||||
<span @click="insertImage" class="iconfont icon-img"></span>
|
||||
</li>
|
||||
<li v-if="tools.table" name="表格">
|
||||
<span
|
||||
@click="insertTable"
|
||||
class="iconfont icon-table"></span>
|
||||
<span @click="insertTable" class="iconfont icon-table"></span>
|
||||
</li>
|
||||
<li v-if="tools.print" name="打印">
|
||||
<span class="iconfont icon-dayin" @click="print"></span>
|
||||
</li>
|
||||
<li v-if="tools.theme" class="shift-theme" name="代码块主题">
|
||||
<div>
|
||||
<span class="iconfont icon-theme" @click="toggleSlideDown"></span>
|
||||
<ul :class="{active:slideDown}">
|
||||
<li @click="setThemes('Light')">Light</li>
|
||||
<li @click="setThemes('Dark')">Dark</li>
|
||||
<li @click="setThemes('OneDark')">OneDark</li>
|
||||
<li @click="setThemes('GitHub')">GitHub</li>
|
||||
<span class="iconfont icon-yanse" @click="toggleSlideDown"></span>
|
||||
<ul :class="{active:slideDown}" @mouseleave="slideDown=false">
|
||||
<li @click="setThemes('Light')"> <span class="iconfont icon-theme"></span><i>Light</i></li>
|
||||
<li @click="setThemes('Dark')"><span class="iconfont icon-vip"></span><i>VS Code</i></li>
|
||||
<li @click="setThemes('OneDark')"><span class="iconfont icon-atom"></span><i>Atom OneDark</i></li>
|
||||
<li @click="setThemes('GitHub')"><span class="iconfont icon-github51"></span><i>GitHub</i></li>
|
||||
</ul>
|
||||
</div>
|
||||
</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="预览">
|
||||
<span @click="preview=2" class="iconfont icon-preview"></span>
|
||||
</li>
|
||||
@ -102,28 +101,14 @@
|
||||
</ul>
|
||||
<!-- 编辑器 -->
|
||||
<div class="markdown-content">
|
||||
<div v-show="preview===1||preview===3" class="markdown-editor" ref="markdownContent" @scroll="markdownScroll"
|
||||
@mouseenter="mousescrollSide('markdown')">
|
||||
<div v-show="preview===1||preview===3" class="markdown-editor" ref="markdownContent" @scroll="markdownScroll" @mouseenter="mousescrollSide('markdown')">
|
||||
<ul class="index" ref="index" :style="{height:scrollHeight?`${scrollHeight}px`:'100%'}">
|
||||
<li v-for="(item,index) in indexLenth">{{index+1}}</li>
|
||||
</ul>
|
||||
<textarea
|
||||
v-model="value"
|
||||
@keydown.tab="tab"
|
||||
@keyup.enter="enter"
|
||||
@keyup.delete="onDelete"
|
||||
ref="textarea"
|
||||
:style="{height:scrollHeight?`${scrollHeight}px`:'100%'}"
|
||||
></textarea>
|
||||
<textarea v-model="value" @keydown.tab="tab" @keyup.enter="enter" @keyup.delete="onDelete" ref="textarea" :style="{height:scrollHeight?`${scrollHeight}px`:'100%'}"></textarea>
|
||||
</div>
|
||||
<div v-show="preview==1" class="empty" style="width:12px;"></div>
|
||||
<div
|
||||
v-show="preview===1||preview===2"
|
||||
:class="`markdown-preview ${themeName}`"
|
||||
v-html="previewMarkdown"
|
||||
ref="preview"
|
||||
@scroll="previewScroll"
|
||||
@mouseenter="mousescrollSide('preview')">
|
||||
<div v-show="preview===1||preview===2" :class="`markdown-preview ${themeName}`" v-html="previewMarkdown" ref="preview" @scroll="previewScroll" @mouseenter="mousescrollSide('preview')">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -133,6 +118,7 @@
|
||||
import markdown from './markdown';
|
||||
|
||||
export default markdown;
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@ -143,5 +129,6 @@
|
||||
@import "css/gitHub";
|
||||
@import "css/common";
|
||||
@import "css/index";
|
||||
@import "font/iconfont.css";
|
||||
@import "./font/iconfont.css";
|
||||
|
||||
</style>
|
||||
|
@ -6,110 +6,112 @@ import Print from './js/print';
|
||||
hljs.initHighlightingOnLoad();
|
||||
|
||||
marked.setOptions({
|
||||
renderer: new marked.Renderer(),
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: true,
|
||||
smartLists: true,
|
||||
highlight: function (code) {
|
||||
renderer : new marked.Renderer(),
|
||||
gfm : true,
|
||||
tables : true,
|
||||
breaks : false,
|
||||
pedantic : false,
|
||||
sanitize : true,
|
||||
smartLists : true,
|
||||
highlight : function (code) {
|
||||
return hljs.highlightAuto(code).value;
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
name: 'markdown',
|
||||
props: {
|
||||
title: { // 标题
|
||||
type: String,
|
||||
default: ''
|
||||
name : 'markdown',
|
||||
props : {
|
||||
title : { // 标题
|
||||
type : String,
|
||||
default : ''
|
||||
},
|
||||
titleStyle: { // 标题样式
|
||||
type: Object,
|
||||
default () {
|
||||
titleStyle : {// 标题样式
|
||||
type : Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
theme: { // 默认主题
|
||||
type: String,
|
||||
default: 'Light'
|
||||
theme : { // 默认主题
|
||||
type : String,
|
||||
default : 'Light'
|
||||
},
|
||||
width: { // 宽度
|
||||
type: [Number, String],
|
||||
default: 'auto'
|
||||
width : {// 宽度
|
||||
type : [Number, String],
|
||||
default : 'auto'
|
||||
},
|
||||
height: { // 高度
|
||||
type: Number,
|
||||
default: 600
|
||||
height : { // 高度
|
||||
type : Number,
|
||||
default : 600
|
||||
}, // 宽度
|
||||
toolbars: { // 工具栏
|
||||
type: Object,
|
||||
default () {
|
||||
toolbars : { // 工具栏
|
||||
type : Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
autoSave: { // 是否自动保存
|
||||
type: Boolean,
|
||||
default: true
|
||||
autoSave : {// 是否自动保存
|
||||
type : Boolean,
|
||||
default : true
|
||||
},
|
||||
interval: { // 自动保存频率 单位:毫秒
|
||||
type: Number,
|
||||
default: 10000
|
||||
interval : { // 自动保存频率 单位:毫秒
|
||||
type : Number,
|
||||
default : 10000
|
||||
},
|
||||
initialValue: { // 初始化值
|
||||
type: String,
|
||||
default: ''
|
||||
initialValue : { // 初始化值
|
||||
type : String,
|
||||
default : ''
|
||||
},
|
||||
mode: { // 模式 1 分屏显示 2 预览详情 3 全屏编辑
|
||||
type: [Number, String],
|
||||
default: 1
|
||||
mode : { // 模式 1 分屏显示 2 预览详情 3 全屏编辑
|
||||
type : [Number, String],
|
||||
default : 1
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: '', // 输入框内容
|
||||
timeoutId: null,
|
||||
hljsInit: null,
|
||||
indexLenth: 1,
|
||||
previewMarkdown: '',
|
||||
preview: 1, // 是否是预览状态
|
||||
isFullscreen: false,
|
||||
scrollHeight: null,
|
||||
scroll: 'markdown', // 哪个半栏在滑动
|
||||
allTools: { // 显示隐藏的工具栏
|
||||
strong: true,
|
||||
italic: true,
|
||||
overline: true,
|
||||
h1: true,
|
||||
h2: true,
|
||||
h3: true,
|
||||
h4: false,
|
||||
h5: false,
|
||||
h6: false,
|
||||
hr: true,
|
||||
quote: true,
|
||||
ul: true,
|
||||
ol: true,
|
||||
code: true,
|
||||
link: true,
|
||||
image: true,
|
||||
table: true,
|
||||
checked: true,
|
||||
notChecked: true,
|
||||
shift: true,
|
||||
fullscreen: true,
|
||||
print: true,
|
||||
theme: true
|
||||
value : '', // 输入框内容
|
||||
timeoutId : null,
|
||||
hljsInit : null,
|
||||
indexLenth : 1,
|
||||
previewMarkdown : '',
|
||||
preview : 1, // 是否是预览状态
|
||||
isFullscreen : false,
|
||||
scrollHeight : null,
|
||||
scroll : 'markdown',// 哪个半栏在滑动
|
||||
allTools : { // 显示隐藏的工具栏
|
||||
strong : true,
|
||||
italic : true,
|
||||
overline : true,
|
||||
h1 : true,
|
||||
h2 : true,
|
||||
h3 : true,
|
||||
h4 : false,
|
||||
h5 : false,
|
||||
h6 : false,
|
||||
hr : true,
|
||||
quote : true,
|
||||
ul : true,
|
||||
ol : true,
|
||||
code : true,
|
||||
link : true,
|
||||
image : true,
|
||||
table : true,
|
||||
checked : true,
|
||||
notChecked : true,
|
||||
shift : true,
|
||||
fullscreen : true,
|
||||
print : false,
|
||||
theme: true,
|
||||
exportmd:true,
|
||||
importmd:true
|
||||
},
|
||||
slideDown: false,
|
||||
themeName: 'Light', // 主题名称
|
||||
lastInsert: '',
|
||||
timerId: null, // 定时器id
|
||||
slideDown : false,
|
||||
themeName : 'Light',// 主题名称
|
||||
lastInsert : '',
|
||||
timerId : null,// 定时器id
|
||||
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
computed : {
|
||||
editorHeight() {
|
||||
if (this.isFullscreen) {
|
||||
return window.innerHeight;
|
||||
@ -118,34 +120,26 @@ export default {
|
||||
}
|
||||
},
|
||||
tools() {
|
||||
const {
|
||||
allTools,
|
||||
toolbars
|
||||
} = this;
|
||||
const {allTools, toolbars} = this;
|
||||
return Object.assign(allTools, toolbars)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$refs.textarea.focus();
|
||||
})
|
||||
this.init();
|
||||
//this.addListener();
|
||||
},
|
||||
methods: {
|
||||
methods : {
|
||||
init() {
|
||||
this.themeName = this.theme;
|
||||
const {
|
||||
autoSave,
|
||||
interval,
|
||||
theme,
|
||||
initialValue,
|
||||
mode
|
||||
} = this;
|
||||
const {autoSave, interval, theme, initialValue, mode} = this;
|
||||
this.value = initialValue;
|
||||
this.preview = mode;
|
||||
this.previewMarkdown = marked(initialValue, {
|
||||
sanitize: true
|
||||
sanitize : true
|
||||
});
|
||||
if (autoSave) {
|
||||
this.timerId = setInterval(() => {
|
||||
@ -174,13 +168,11 @@ export default {
|
||||
markdownContent.scrollTop = parseInt((previewScrollTop / previewScrollHeight) * markdownScrollHeight);
|
||||
}
|
||||
},
|
||||
mousescrollSide(side) { // 设置究竟是哪个半边在主动滑动
|
||||
mousescrollSide(side) {// 设置究竟是哪个半边在主动滑动
|
||||
this.scroll = side;
|
||||
},
|
||||
insertContent(str) { // 插入文本
|
||||
const {
|
||||
preview
|
||||
} = this;
|
||||
const {preview} = this;
|
||||
if (preview === 2) {
|
||||
return;
|
||||
}
|
||||
@ -202,7 +194,7 @@ export default {
|
||||
if (document.selection) {
|
||||
textDom.focus();
|
||||
let selectRange = document.selection.createRange();
|
||||
selectRange.moveStart('character', -this.value.length);
|
||||
selectRange.moveStart('character', - this.value.length);
|
||||
cursorPos = selectRange.text.length;
|
||||
} else if (textDom.selectionStart || textDom.selectionStart == '0') {
|
||||
cursorPos = textDom.selectionStart;
|
||||
@ -250,12 +242,12 @@ export default {
|
||||
},
|
||||
insertTitle(level) { // 插入标题
|
||||
const titleLevel = {
|
||||
1: '\n# ',
|
||||
2: '\n## ',
|
||||
3: '\n### ',
|
||||
4: '\n#### ',
|
||||
5: '\n##### ',
|
||||
6: '\n###### '
|
||||
1 : '\n# ',
|
||||
2 : '\n## ',
|
||||
3 : '\n### ',
|
||||
4 : '\n#### ',
|
||||
5 : '\n##### ',
|
||||
6 : '\n###### '
|
||||
};
|
||||
this.insertContent(titleLevel[level]);
|
||||
},
|
||||
@ -351,12 +343,12 @@ export default {
|
||||
},
|
||||
insertTitle(level) { // 插入标题
|
||||
const titleLevel = {
|
||||
1: '# ',
|
||||
2: '## ',
|
||||
3: '### ',
|
||||
4: '#### ',
|
||||
5: '##### ',
|
||||
6: '###### '
|
||||
1 : '# ',
|
||||
2 : '## ',
|
||||
3 : '### ',
|
||||
4 : '#### ',
|
||||
5 : '##### ',
|
||||
6 : '###### '
|
||||
};
|
||||
this.insertContent(titleLevel[level]);
|
||||
},
|
||||
@ -374,25 +366,23 @@ export default {
|
||||
},
|
||||
handleSave() { // 保存操作
|
||||
this.$emit('on-save', {
|
||||
markdownValue: this.value,
|
||||
htmlValue: this.previewMarkdown,
|
||||
theme: this.themeName
|
||||
markdownValue : this.value,
|
||||
htmlValue : this.previewMarkdown,
|
||||
theme : this.theme
|
||||
});
|
||||
},
|
||||
insertLine() { // 插入分割线
|
||||
this.insertContent(`\n----\n`);
|
||||
},
|
||||
toggleSlideDown() { // 显示主题选项
|
||||
this.slideDown = !this.slideDown;
|
||||
this.slideDown = ! this.slideDown;
|
||||
},
|
||||
setThemes(name) { // 设置主题
|
||||
this.themeName = name;
|
||||
this.slideDown = false;
|
||||
},
|
||||
enter(e) { // 回车事件
|
||||
const {
|
||||
lastInsert
|
||||
} = this;
|
||||
const {lastInsert} = this;
|
||||
const list = ['- ', '1. ', '- [ ] ', '- [x] ']
|
||||
if (list.includes(lastInsert)) {
|
||||
this.insertContent(lastInsert);
|
||||
@ -410,26 +400,57 @@ export default {
|
||||
},
|
||||
addListener() { // 事件监听,阻止保存
|
||||
this.removeListener();
|
||||
document.addEventListener('keydown', this.listener)
|
||||
document.addEventListener('keydown',this.listener)
|
||||
},
|
||||
removeListener() {
|
||||
document.removeEventListener('keydown', this.listener)
|
||||
removeListener(){
|
||||
document.removeEventListener('keydown',this.listener)
|
||||
},
|
||||
listener(e) {
|
||||
listener(e){
|
||||
if (e.keyCode === 83) {
|
||||
if (e.metaKey || e.ctrlKey) {
|
||||
if( e.metaKey||e.ctrlKey){
|
||||
e.preventDefault();
|
||||
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 = '';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
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 : {
|
||||
value() {
|
||||
clearTimeout(this.timeoutId);
|
||||
this.timeoutId = setTimeout(() => {
|
||||
this.previewMarkdown = marked(this.value, {
|
||||
sanitize: true
|
||||
sanitize : true
|
||||
});
|
||||
}, 30)
|
||||
this.indexLenth = this.value.split('\n').length;
|
||||
@ -437,12 +458,6 @@ export default {
|
||||
const height_2 = this.$refs.textarea.scrollHeight;
|
||||
const height_3 = this.$refs.preview.scrollHeight;
|
||||
this.scrollHeight = Math.max(height_1, height_2, height_3);
|
||||
},
|
||||
initialValue() {
|
||||
this.value = this.initialValue;
|
||||
},
|
||||
theme() {
|
||||
this.themeName = this.theme;
|
||||
}
|
||||
},
|
||||
destroyed() { // 销毁时清除定时器
|
||||
|
@ -2,11 +2,11 @@ var path = require('path')
|
||||
var webpack = require('webpack')
|
||||
|
||||
module.exports = {
|
||||
entry: './src/main.js',
|
||||
entry: './src/main.js', // 打包为npm包时将此处修改为 ./src/index.js
|
||||
output: {
|
||||
path: path.resolve(__dirname, './dist'),
|
||||
publicPath: '/dist/',
|
||||
filename: 'build.js',
|
||||
filename: 'build.js',// 打包为npm包时将此处修改为 index.js
|
||||
libraryTarget: 'umd',
|
||||
library: 'markdown-vue',
|
||||
umdNamedDefine: true
|
||||
|