chore:优化代码,减少代码体积,增加部分功能
This commit is contained in:
parent
214bae06f6
commit
d007de2fae
85
README.md
85
README.md
@ -1,14 +1,16 @@
|
||||
# vue-Markdown编辑器
|
||||
|
||||
[在线示例地址](http://47.99.49.57/markdown/)
|
||||
[在线示例地址](https://zhaoxuhui1122.github.io/vue-markdown/)
|
||||
|
||||
GitHub :[ https://github.com/coinsuper/vue-markdown]( https://github.com/coinsuper/vue-markdown)
|
||||
GitHub :[https://github.com/zhaoxuhui1122/vue-markdown]( https://github.com/zhaoxuhui1122/vue-markdown)
|
||||
|
||||
|
||||
### 1.简介
|
||||
|
||||
**一款使用marked和highlight.js开发的一款markdown编辑器,目前只支持在vue项目中使用。
|
||||
编辑器涵盖了常用的markdown编辑器功能,工具栏可自定义配置,也可进行二次开发。**
|
||||
**一款使用marked和highlight.js开发的一款markdown编辑器,目前只支持在vue项目中使用。**
|
||||
|
||||
**编辑器涵盖了常用的markdown编辑器功能,工具栏可自定义配置,也可进行二次开发。**
|
||||
|
||||
|
||||
**效果**
|
||||
![image](http://smalleyes.oss-cn-shanghai.aliyuncs.com/WechatIMG586.png)
|
||||
@ -17,6 +19,10 @@ GitHub :[ https://github.com/coinsuper/vue-markdown]( https://github.com/coinsup
|
||||
|
||||
```
|
||||
npm i -S vue-meditor
|
||||
|
||||
或
|
||||
|
||||
直接复制对应的组件到项目目录内 (推荐)
|
||||
```
|
||||
|
||||
### 3.在项目中使用
|
||||
@ -40,8 +46,6 @@ components:{
|
||||
|
||||
名称 | 类型|说明|默认值
|
||||
---|---|---|---
|
||||
title|String|编辑器标题,默认为空,不显示
|
||||
titleStyle|Object|标题样式,如果自定义标题时可自行编写样式
|
||||
initialValue|String|编辑器初始化内容
|
||||
width|Number|编辑器宽度|
|
||||
height|Number|编辑器高度,单位 px|600
|
||||
@ -49,14 +53,14 @@ theme|String|代码块主题配置,共有四个值,分别为Light、Dark、O
|
||||
autoSave|Boolean|是否自动保存|true
|
||||
interval|Number|自动保存频率,单位毫秒|10000
|
||||
toolbars|Object|工具栏配置,具体功能详见工具栏功能配置表
|
||||
mode|Number|初始化显示模式 1 分屏显示 2 预览详情 3 全屏编辑
|
||||
exportFileName|String|导出文件的名称|未命名文件
|
||||
|
||||
### 5.events
|
||||
|
||||
名称 | 说明
|
||||
---|---
|
||||
on-save|自动保存或者手动保存时触发,返回当前编辑器内原始输入内容和转以后的内容
|
||||
|
||||
on-paste-image|粘贴图片,返回当前粘贴的file文件
|
||||
### 6.工具栏配置
|
||||
|
||||
名称 | 说明 | 默认显示
|
||||
@ -91,10 +95,16 @@ importmd|导入本地*.md文件|是
|
||||
**关于保存时返回值**
|
||||
|
||||
```
|
||||
markdownValue // 编辑器输入的原始内容
|
||||
htmlValue // 右侧现实的问转义后的内容
|
||||
value // 编辑器输入的原始内容
|
||||
html // 右侧现实的问转义后的内容
|
||||
theme // 保存时的主题名字
|
||||
```
|
||||
**标题配置**
|
||||
|
||||
```
|
||||
支持配置编辑器名称,提供了name=title的slot插槽
|
||||
```
|
||||
|
||||
|
||||
**工具栏配置**
|
||||
|
||||
@ -105,43 +115,38 @@ const config = {
|
||||
}
|
||||
<MarkDown :toolbars="config"/>
|
||||
```
|
||||
**优化代码体积**
|
||||
|
||||
|
||||
**关于二次开发**
|
||||
|
||||
原始文件在src/markdown下,可在其基础上自定义开发,也可将markdown文件夹
|
||||
|
||||
复制到新项目中,安装对应依赖 highlight.js和marked即可
|
||||
|
||||
**关于二次开发后打包**
|
||||
|
||||
```
|
||||
// 修改webpack.config.js
|
||||
|
||||
entry: './src/main.js', // main.js改为index.js
|
||||
output: {
|
||||
path: path.resolve(__dirname, './dist'),
|
||||
publicPath: '/dist/',
|
||||
filename: 'build.js', // 输出文件名改为index.js或其他
|
||||
libraryTarget: 'umd',
|
||||
library: 'markdown-vue',
|
||||
umdNamedDefine: true
|
||||
},
|
||||
|
||||
项目中为了达到代码高亮显示,需要用到highlight.js,
|
||||
由于highlight.js体积过于庞大,项目中按需加载了部分常用的程序语言,
|
||||
可根据需求自行配置,配置目录位于/markdown/js/hljs内
|
||||
```
|
||||
|
||||
|
||||
### 更新日志
|
||||
v0.7.0
|
||||
|
||||
1.修复主题无法更新的问题
|
||||
2.修复文档初始化值无法动态切换的问题
|
||||
**v1.2.0**
|
||||
1. 优化代码体积,按需加载highlight.js,较少了三分之二的代码体积
|
||||
2. 新增图片粘贴功能
|
||||
3. 增加图片预览功能
|
||||
4. 修复部分bug
|
||||
|
||||
**v0.9.3**
|
||||
|
||||
1. 解决初始化值initialValue无法动态改变的问题
|
||||
2. 修改了打包配置
|
||||
|
||||
**v0.8.0**
|
||||
|
||||
1. 新增md文件导出和读取功能
|
||||
2. 修改预览部分样式
|
||||
3. 修改头部菜单样式
|
||||
|
||||
**v0.7.0**
|
||||
|
||||
1. 修复主题无法更新的问题
|
||||
2. 修复文档初始化值无法动态切换的问题
|
||||
|
||||
v0.8.0
|
||||
1.新增md文件导出和读取功能
|
||||
2.修改预览部分样式
|
||||
3.修改头部菜单样式
|
||||
|
||||
v0.9.3
|
||||
1.解决初始化值initialValue无法动态改变的问题
|
||||
2.修改了打包配置
|
||||
|
Binary file not shown.
BIN
build/iconfont.eot.gz
Normal file
BIN
build/iconfont.eot.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 30 KiB |
BIN
build/iconfont.svg.gz
Normal file
BIN
build/iconfont.svg.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
build/iconfont.ttf.gz
Normal file
BIN
build/iconfont.ttf.gz
Normal file
Binary file not shown.
BIN
build/iconfont.woff
Normal file
BIN
build/iconfont.woff
Normal file
Binary file not shown.
1274
build/index.js
1274
build/index.js
File diff suppressed because one or more lines are too long
BIN
build/index.js.gz
Normal file
BIN
build/index.js.gz
Normal file
Binary file not shown.
1
build/index.js.map
Normal file
1
build/index.js.map
Normal file
File diff suppressed because one or more lines are too long
BIN
build/index.js.map.gz
Normal file
BIN
build/index.js.map.gz
Normal file
Binary file not shown.
8
dist/build.js
vendored
8
dist/build.js
vendored
File diff suppressed because one or more lines are too long
BIN
dist/build.js.gz
vendored
Normal file
BIN
dist/build.js.gz
vendored
Normal file
Binary file not shown.
2
dist/build.js.map
vendored
2
dist/build.js.map
vendored
File diff suppressed because one or more lines are too long
BIN
dist/build.js.map.gz
vendored
Normal file
BIN
dist/build.js.map.gz
vendored
Normal file
Binary file not shown.
BIN
dist/iconfont.eot
vendored
BIN
dist/iconfont.eot
vendored
Binary file not shown.
116
dist/iconfont.svg
vendored
116
dist/iconfont.svg
vendored
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 49 KiB |
BIN
dist/iconfont.ttf
vendored
BIN
dist/iconfont.ttf
vendored
Binary file not shown.
2
dist/index.js
vendored
2
dist/index.js
vendored
File diff suppressed because one or more lines are too long
1
dist/index.js.map
vendored
1
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
18
index.html
18
index.html
@ -1,11 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>template</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="./dist/build.js"></script>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>markdown编辑器组件</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="./dist/build.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
10
package.json
10
package.json
@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "vue-meditor",
|
||||
"description": "一款使用marked和highlight.js开发的一款markdown编辑器",
|
||||
"version": "0.9.3",
|
||||
"version": "1.2.0",
|
||||
"author": "zhaoxuhui<1258835133@qq.com>",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"main": "build/index.js",
|
||||
"keywords": [
|
||||
"markdown编辑器",
|
||||
"vue markdown编辑器",
|
||||
@ -18,7 +18,8 @@
|
||||
"dependencies": {
|
||||
"highlight.js": "^9.12.0",
|
||||
"marked": "^0.4.0",
|
||||
"vue": "^2.5.11"
|
||||
"vue": "^2.5.11",
|
||||
"vue-meditor": "^1.1.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
@ -30,8 +31,9 @@
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.0.5",
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^0.28.11",
|
||||
"compression-webpack-plugin": "^1.1.11",
|
||||
"file-loader": "^1.1.4",
|
||||
"less": "^3.0.4",
|
||||
"less-loader": "^4.1.0",
|
||||
|
92
src/App.vue
92
src/App.vue
@ -1,40 +1,86 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<div class="container">
|
||||
<mark-down @on-save="save" theme="OneDark" :initialValue="initialValue"></mark-down>
|
||||
<div id="app">
|
||||
<div class="container">
|
||||
<h1>vue-markdown编辑器组件</h1>
|
||||
<a target="_blank" href="https://zhaoxuhui1122.github.io/vue-markdown/">使用文档</a>
|
||||
<ul>
|
||||
<li></li>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>
|
||||
<div class="content">
|
||||
<mark-down @on-save="save" theme="OneDark" :initialValue="initialValue"></mark-down>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import MarkDown from './markdown/index' // 开发文件
|
||||
import MarkDown from "../build"; // 引入打包好的文件
|
||||
export default {
|
||||
// import MarkDown from './markdown/index' // 开发文件
|
||||
// import MarkDown from "../build"; // 引入打包好的文件
|
||||
import MarkDown from 'vue-meditor';
|
||||
|
||||
import doc from './doc';
|
||||
export default {
|
||||
name: "app",
|
||||
components: {
|
||||
MarkDown
|
||||
MarkDown
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialValue: ""
|
||||
};
|
||||
return {
|
||||
initialValue: ""
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
save(res) {
|
||||
console.log(res);
|
||||
}
|
||||
save(res) {
|
||||
console.log(res);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(() => {
|
||||
this.initialValue = "# vue-markdown";
|
||||
}, 1000);
|
||||
setTimeout(() => {
|
||||
this.initialValue = doc;
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
margin: 20px auto;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
<style scoped lang="less">
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
|
||||
h1 {
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
background-color: #f8f8f9;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #edeff0;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
text-decoration: none;
|
||||
color: #2d8cf0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
153
src/doc.js
Normal file
153
src/doc.js
Normal file
@ -0,0 +1,153 @@
|
||||
export default `# vue-Markdown编辑器
|
||||
|
||||
[在线示例地址](https://zhaoxuhui1122.github.io/vue-markdown/)
|
||||
|
||||
GitHub :[https://github.com/zhaoxuhui1122/vue-markdown]( https://github.com/zhaoxuhui1122/vue-markdown)
|
||||
|
||||
|
||||
### 1.简介
|
||||
|
||||
**一款使用marked和highlight.js开发的一款markdown编辑器,目前只支持在vue项目中使用。**
|
||||
|
||||
**编辑器涵盖了常用的markdown编辑器功能,工具栏可自定义配置,也可进行二次开发。**
|
||||
|
||||
|
||||
**效果**
|
||||
![image](http://smalleyes.oss-cn-shanghai.aliyuncs.com/WechatIMG586.png)
|
||||
|
||||
### 2.安装
|
||||
|
||||
\`\`\`
|
||||
npm i -S vue-meditor
|
||||
|
||||
或
|
||||
|
||||
直接复制对应的组件到项目目录内 (推荐)
|
||||
\`\`\`
|
||||
|
||||
### 3.在项目中使用
|
||||
|
||||
|
||||
\`\`\`
|
||||
import MarkDown from 'vue-meditor'
|
||||
|
||||
...
|
||||
components:{
|
||||
MarkDown
|
||||
}
|
||||
...
|
||||
|
||||
<template>
|
||||
<mark-down/>
|
||||
</template>
|
||||
\`\`\`
|
||||
|
||||
### 4.props
|
||||
|
||||
名称 | 类型|说明|默认值
|
||||
---|---|---|---
|
||||
initialValue|String|编辑器初始化内容
|
||||
width|Number|编辑器宽度|
|
||||
height|Number|编辑器高度,单位 px|600
|
||||
theme|String|代码块主题配置,共有四个值,分别为Light、Dark、OneDark、GitHub|Light
|
||||
autoSave|Boolean|是否自动保存|true
|
||||
interval|Number|自动保存频率,单位毫秒|10000
|
||||
toolbars|Object|工具栏配置,具体功能详见工具栏功能配置表
|
||||
exportFileName|String|导出文件的名称|未命名文件
|
||||
|
||||
### 5.events
|
||||
|
||||
名称 | 说明
|
||||
---|---
|
||||
on-save|自动保存或者手动保存时触发,返回当前编辑器内原始输入内容和转以后的内容
|
||||
on-paste-image|粘贴图片,返回当前粘贴的file文件
|
||||
### 6.工具栏配置
|
||||
|
||||
名称 | 说明 | 默认显示
|
||||
---|---|---
|
||||
strong|粗体|是
|
||||
italic|斜体|是
|
||||
overline |删除线|是
|
||||
h1 |标题1|是
|
||||
h2 |标题2|是
|
||||
h3 |标题3|是
|
||||
h4|标题4|否
|
||||
h5 |标题5|否
|
||||
h6 |标题6|否
|
||||
hr |分割线|是
|
||||
quote|引用|是
|
||||
ul |无序列表|是
|
||||
ol|有序列表|是
|
||||
code |代码块|是
|
||||
link |链接|是
|
||||
image|image|是
|
||||
table |表格|是
|
||||
checked|已完成列表|是
|
||||
notChecked |未完成列表|是
|
||||
shift|预览|是
|
||||
print |打印|否
|
||||
theme|主题切换|是
|
||||
fullscreen |全屏|是
|
||||
exportmd|导出为*.md文件|是
|
||||
importmd|导入本地*.md文件|是
|
||||
|
||||
### 7.其他说明
|
||||
**关于保存时返回值**
|
||||
|
||||
\`\`\`
|
||||
value // 编辑器输入的原始内容
|
||||
html // 右侧现实的问转义后的内容
|
||||
theme // 保存时的主题名字
|
||||
\`\`\`
|
||||
**标题配置**
|
||||
|
||||
\`\`\`
|
||||
支持配置编辑器名称,提供了name=title的slot插槽
|
||||
\`\`\`
|
||||
|
||||
|
||||
**工具栏配置**
|
||||
|
||||
\`\`\`
|
||||
// 例:
|
||||
const config = {
|
||||
print:false // 隐藏掉打印功能
|
||||
}
|
||||
<MarkDown :toolbars="config"/>
|
||||
\`\`\`
|
||||
**优化代码体积**
|
||||
|
||||
|
||||
\`\`\`
|
||||
项目中为了达到代码高亮显示,需要用到highlight.js,
|
||||
由于highlight.js体积过于庞大,项目中按需加载了部分常用的程序语言,
|
||||
可根据需求自行配置,配置目录位于/markdown/js/hljs内
|
||||
\`\`\`
|
||||
|
||||
|
||||
### 更新日志
|
||||
|
||||
**v1.0.0**
|
||||
1. 优化代码体积,按需加载highlight.js,较少了三分之二的代码体积
|
||||
2. 新增图片粘贴功能
|
||||
3. 增加图片预览功能
|
||||
4. 修复部分bug
|
||||
|
||||
**v0.9.3**
|
||||
|
||||
1. 解决初始化值initialValue无法动态改变的问题
|
||||
2. 修改了打包配置
|
||||
|
||||
**v0.8.0**
|
||||
|
||||
1. 新增md文件导出和读取功能
|
||||
2. 修改预览部分样式
|
||||
3. 修改头部菜单样式
|
||||
|
||||
**v0.7.0**
|
||||
|
||||
1. 修复主题无法更新的问题
|
||||
2. 修复文档初始化值无法动态切换的问题
|
||||
|
||||
|
||||
`
|
File diff suppressed because one or more lines are too long
@ -15,11 +15,9 @@
|
||||
border-radius: 4px;
|
||||
margin: 20px 0 !important;
|
||||
overflow-x: auto !important;
|
||||
|
||||
* {
|
||||
font-family: Consolas !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
|
File diff suppressed because one or more lines are too long
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: 49 KiB After Width: | Height: | Size: 30 KiB |
Binary file not shown.
Binary file not shown.
BIN
src/markdown/font/iconfont.woff2
Normal file
BIN
src/markdown/font/iconfont.woff2
Normal file
Binary file not shown.
@ -1,8 +1,12 @@
|
||||
<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="{width:editorWidth+'px',height:editorHeight+'px'}"
|
||||
>
|
||||
<!-- 头部工具栏 -->
|
||||
<ul class="markdown-toolbars">
|
||||
<li class="title" v-if="title" :style="{titleStyle}">{{title}}</li>
|
||||
<li><slot name="title"/></li>
|
||||
<li v-if="tools.strong" name="粗体">
|
||||
<span @click="insertStrong" class="iconfont icon-strong"></span>
|
||||
</li>
|
||||
@ -12,29 +16,29 @@
|
||||
<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)"class="title">h1</span>
|
||||
<li v-if="tools.h1" name="标题1">
|
||||
<span style="font-size: 16px;" @click="insertTitle(1)">h1</span>
|
||||
</li>
|
||||
<li v-if="tools.h2" name="标题2">
|
||||
<span @click="insertTitle(2)"class="title">h2</span>
|
||||
<span style="font-size: 16px;" @click="insertTitle(2)">h2</span>
|
||||
</li>
|
||||
<li v-if="tools.h3" name="标题3" >
|
||||
<span @click="insertTitle(3)"class="title">h3</span>
|
||||
<li v-if="tools.h3" name="标题3">
|
||||
<span style="font-size: 16px;" @click="insertTitle(3)">h3</span>
|
||||
</li>
|
||||
<li v-if="tools.h4" name="标题4" >
|
||||
<span @click="insertTitle(4)"class="title">h4</span>
|
||||
<li v-if="tools.h4" name="标题4">
|
||||
<span style="font-size: 16px;" @click="insertTitle(4)">h4</span>
|
||||
</li>
|
||||
<li v-if="tools.h5" name="标题5" >
|
||||
<span @click="insertTitle(5)"class="title">h5</span>
|
||||
<li v-if="tools.h5" name="标题5">
|
||||
<span style="font-size: 16px;" @click="insertTitle(5)">h5</span>
|
||||
</li>
|
||||
<li v-if="tools.h6" name="标题6">
|
||||
<span @click="insertTitle(6)"class="title">h6</span>
|
||||
<span style="font-size: 16px;" @click="insertTitle(6)">h6</span>
|
||||
</li>
|
||||
<li v-if="tools.hr" name="分割线">
|
||||
<span @click="insertLine" class="iconfont icon-horizontal"></span>
|
||||
</li>
|
||||
<li v-if="tools.quote" name="引用">
|
||||
<span @click="insertQuote" class="iconfont icon-quote"></span>
|
||||
<span style="font-size: 16px;" @click="insertQuote" class="iconfont icon-quote"></span>
|
||||
</li>
|
||||
<li v-if="tools.ul" name="无序列表">
|
||||
<span @click="insertUl" class="iconfont icon-ul"></span>
|
||||
@ -65,31 +69,35 @@
|
||||
</li>
|
||||
<li v-if="tools.theme" class="shift-theme" name="代码块主题">
|
||||
<div>
|
||||
<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>
|
||||
<span class="iconfont icon-yanse" @click="themeSlideDown=!themeSlideDown"></span>
|
||||
<ul :class="{active:themeSlideDown}" @mouseleave="themeSlideDown=false">
|
||||
<li @click="setThemes('Light')">Light</li>
|
||||
<li @click="setThemes('Dark')">VS Code</li>
|
||||
<li @click="setThemes('OneDark')">Atom OneDark</li>
|
||||
<li @click="setThemes('GitHub')">GitHub</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li name="导入本地文件" class="import-file" v-show="tools.importmd">
|
||||
<span class="iconfont icon-daoru"></span>
|
||||
<span class="iconfont icon-daoru" @click="importFile"></span>
|
||||
<input type="file" @change="importFile($event)" accept="text/markdown">
|
||||
</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>
|
||||
<li v-if="tools.shift&&preview==2" name="编辑">
|
||||
<li v-if="tools.shift&&preview==2" name="全屏编辑">
|
||||
<span @click="preview=3" class="iconfont icon-md"></span>
|
||||
</li>
|
||||
<li v-if="tools.shift&&preview==3" name="分屏显示">
|
||||
<span @click="preview=1" class="iconfont icon-group"></span>
|
||||
</li>
|
||||
<li v-if="tools.shift&&preview==1" name="预览">
|
||||
<span @click="preview=2" class="iconfont icon-preview"></span>
|
||||
</li>
|
||||
<li :name="scrolling?'同步滚动:开':'同步滚动:关'">
|
||||
<span @click="scrolling=!scrolling" v-show="scrolling" class="iconfont icon-on"></span>
|
||||
<span @click="scrolling=!scrolling" v-show="!scrolling" class="iconfont icon-off"></span>
|
||||
</li>
|
||||
<li class="empty"></li>
|
||||
|
||||
<li v-if="tools.fullscreen&&!isFullscreen" name="全屏">
|
||||
@ -100,17 +108,38 @@
|
||||
</li>
|
||||
</ul>
|
||||
<!-- 编辑器 -->
|
||||
<div class="markdown-content">
|
||||
<div v-show="preview===1||preview===3" class="markdown-editor" ref="markdownContent" @scroll="markdownScroll" @mouseenter="mousescrollSide('markdown')">
|
||||
<div class="markdown-content" :style="{background:preview==2?'#fff':''}">
|
||||
<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}`"
|
||||
ref="preview"
|
||||
@scroll="previewScroll"
|
||||
@mouseenter="mousescrollSide('preview')">
|
||||
<div v-html="html"
|
||||
ref="previewInner" ></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 预览图片-->
|
||||
<div :class="['preview-img',previewImgModal?'active':'']">
|
||||
<span class="close" @click="previewImgModal=false">关闭</span>
|
||||
<img :src="previewImgSrc" :class="[previewImgMode]" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -118,17 +147,14 @@
|
||||
import markdown from './markdown';
|
||||
|
||||
export default markdown;
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import "css/theme";
|
||||
@import "font/iconfont.css";
|
||||
@import "./css/theme";
|
||||
@import "css/light";
|
||||
@import "css/dark";
|
||||
@import "css/oneDark";
|
||||
@import "css/one-dark";
|
||||
@import "css/gitHub";
|
||||
@import "css/common";
|
||||
@import "css/index";
|
||||
@import "./font/iconfont.css";
|
||||
|
||||
</style>
|
||||
|
40
src/markdown/js/hljs.js
Normal file
40
src/markdown/js/hljs.js
Normal file
@ -0,0 +1,40 @@
|
||||
//hljs体积过大,多数为解决代码高亮显示的问题,所以只引入部分语言,如果需要可自行加载
|
||||
|
||||
import hljs from 'highlight.js/lib/highlight'
|
||||
|
||||
import javascript from 'highlight.js/lib/languages/javascript'
|
||||
import java from 'highlight.js/lib/languages/java';
|
||||
import css from 'highlight.js/lib/languages/css';
|
||||
import less from 'highlight.js/lib/languages/less';
|
||||
import json from 'highlight.js/lib/languages/json';
|
||||
import go from 'highlight.js/lib/languages/go';
|
||||
import markdown from 'highlight.js/lib/languages/markdown';
|
||||
import php from 'highlight.js/lib/languages/php';
|
||||
import python from 'highlight.js/lib/languages/python';
|
||||
import ruby from 'highlight.js/lib/languages/ruby';
|
||||
import rust from 'highlight.js/lib/languages/rust';
|
||||
import stylus from 'highlight.js/lib/languages/stylus';
|
||||
import typescript from 'highlight.js/lib/languages/typescript';
|
||||
import xml from 'highlight.js/lib/languages/xml';
|
||||
|
||||
const languagesMap = {
|
||||
javascript,
|
||||
java,
|
||||
css,
|
||||
less,
|
||||
json,
|
||||
markdown,
|
||||
go,
|
||||
php,
|
||||
python,
|
||||
ruby,
|
||||
rust,
|
||||
stylus,
|
||||
typescript,
|
||||
xml
|
||||
}
|
||||
Object.keys(languagesMap).forEach(language => {
|
||||
hljs.registerLanguage(language,languagesMap[language])
|
||||
})
|
||||
|
||||
export default hljs;
|
@ -1,119 +0,0 @@
|
||||
var Print = function (dom, options) {
|
||||
if (! (this instanceof Print)) return new Print(dom, options);
|
||||
|
||||
this.options = this.extend({
|
||||
'noPrint' : '.no-print'
|
||||
}, options);
|
||||
|
||||
if ((typeof dom) === 'string') {
|
||||
this.dom = document.querySelector(dom);
|
||||
} else {
|
||||
this.dom = dom;
|
||||
}
|
||||
|
||||
this.init();
|
||||
};
|
||||
Print.prototype = {
|
||||
init : function () {
|
||||
var content = this.getStyle() + this.getHtml();
|
||||
this.writeIframe(content);
|
||||
},
|
||||
extend : function (obj, obj2) {
|
||||
for (var k in obj2) {
|
||||
obj[k] = obj2[k];
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
getStyle : function () {
|
||||
var str = '',
|
||||
styles = document.querySelectorAll('style,link');
|
||||
for (var i = 0 ; i<styles.length ; i ++) {
|
||||
str += styles[i].outerHTML;
|
||||
}
|
||||
str += '<style>' + (this.options.noPrint ? this.options.noPrint: '.no-print') + '{display:none;}</style>';
|
||||
|
||||
return str;
|
||||
},
|
||||
|
||||
getHtml : function () {
|
||||
var inputs = document.querySelectorAll('input');
|
||||
var textareas = document.querySelectorAll('textarea');
|
||||
var selects = document.querySelectorAll('select');
|
||||
|
||||
for (var k in inputs) {
|
||||
if (inputs[k].type == 'checkbox' || inputs[k].type == 'radio') {
|
||||
if (inputs[k].checked == true) {
|
||||
inputs[k].setAttribute('checked', 'checked')
|
||||
} else {
|
||||
inputs[k].removeAttribute('checked')
|
||||
}
|
||||
} else if (inputs[k].type == 'text') {
|
||||
inputs[k].setAttribute('value', inputs[k].value)
|
||||
}
|
||||
}
|
||||
|
||||
for (var k2 in textareas) {
|
||||
if (textareas[k2].type == 'textarea') {
|
||||
textareas[k2].innerHTML = textareas[k2].value
|
||||
}
|
||||
}
|
||||
|
||||
for (var k3 in selects) {
|
||||
if (selects[k3].type == 'select-one') {
|
||||
var child = selects[k3].children;
|
||||
for (var i in child) {
|
||||
if (child[i].tagName == 'OPTION') {
|
||||
if (child[i].selected == true) {
|
||||
child[i].setAttribute('selected', 'selected')
|
||||
} else {
|
||||
child[i].removeAttribute('selected')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.dom.outerHTML;
|
||||
},
|
||||
|
||||
writeIframe : function (content) {
|
||||
var w, doc, iframe = document.createElement('iframe'),
|
||||
f = document.body.appendChild(iframe);
|
||||
iframe.id = 'myIframe';
|
||||
iframe.style = 'position:absolute;width:0;height:0;top:-10px;left:-10px;';
|
||||
|
||||
w = f.contentWindow || f.contentDocument;
|
||||
doc = f.contentDocument || f.contentWindow.document;
|
||||
doc.open();
|
||||
doc.write(content);
|
||||
doc.close();
|
||||
this.toPrint(w);
|
||||
|
||||
setTimeout(function () {
|
||||
document.body.removeChild(iframe)
|
||||
}, 100)
|
||||
},
|
||||
|
||||
toPrint : function (frameWindow) {
|
||||
try {
|
||||
setTimeout(function () {
|
||||
frameWindow.focus();
|
||||
try {
|
||||
if (! frameWindow.document.execCommand('print', false, null)) {
|
||||
frameWindow.print();
|
||||
}
|
||||
} catch (e) {
|
||||
frameWindow.print();
|
||||
}
|
||||
frameWindow.close();
|
||||
}, 10);
|
||||
} catch (err) {
|
||||
console.log('err', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default function _Print(dom) {
|
||||
Print(dom);
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
// 获取光标位置
|
||||
function getCursortPosition(textDom) {
|
||||
var cursorPos = 0;
|
||||
if (document.selection) {
|
||||
// IE Support
|
||||
textDom.focus();
|
||||
var selectRange = document.selection.createRange();
|
||||
selectRange.moveStart('character', -textDom.value.length);
|
||||
cursorPos = selectRange.text.length;
|
||||
} else if (textDom.selectionStart || textDom.selectionStart == '0') {
|
||||
// Firefox support
|
||||
cursorPos = textDom.selectionStart;
|
||||
}
|
||||
return cursorPos;
|
||||
}
|
||||
|
||||
// 设置光标位置
|
||||
function setCaretPosition(textDom, pos) {
|
||||
if (textDom.setSelectionRange) {
|
||||
// IE Support
|
||||
textDom.focus();
|
||||
textDom.setSelectionRange(pos, pos);
|
||||
} else if (textDom.createTextRange) {
|
||||
// Firefox support
|
||||
var range = textDom.createTextRange();
|
||||
range.collapse(true);
|
||||
range.moveEnd('character', pos);
|
||||
range.moveStart('character', pos);
|
||||
range.select();
|
||||
}
|
||||
}
|
||||
// 获取选中文字
|
||||
function getSelectText() {
|
||||
var userSelection,
|
||||
text;
|
||||
if (window.getSelection) {
|
||||
// Firefox support
|
||||
userSelection = window.getSelection();
|
||||
} else if (document.selection) {
|
||||
// IE Support
|
||||
userSelection = document.selection.createRange();
|
||||
}
|
||||
if (!(text = userSelection.text)) {
|
||||
text = userSelection;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中特定范围的文本
|
||||
* 参数:
|
||||
* textDom [JavaScript DOM String] 当前对象
|
||||
* startPos [Int] 起始位置
|
||||
* endPos [Int] 终点位置
|
||||
*/
|
||||
function setSelectText(textDom, startPos, endPos) {
|
||||
var startPos = parseInt(startPos),
|
||||
endPos = parseInt(endPos),
|
||||
textLength = textDom.value.length;
|
||||
if (textLength) {
|
||||
if (!startPos) {
|
||||
startPos = 0;
|
||||
}
|
||||
if (!endPos) {
|
||||
endPos = textLength;
|
||||
}
|
||||
if (startPos > textLength) {
|
||||
startPos = textLength;
|
||||
}
|
||||
if (endPos > textLength) {
|
||||
endPos = textLength;
|
||||
}
|
||||
if (startPos < 0) {
|
||||
startPos = textLength + startPos;
|
||||
}
|
||||
if (endPos < 0) {
|
||||
endPos = textLength + endPos;
|
||||
}
|
||||
if (textDom.createTextRange) {
|
||||
// IE Support
|
||||
var range = textDom.createTextRange();
|
||||
range.moveStart("character", -textLength);
|
||||
range.moveEnd("character", -textLength);
|
||||
range.moveStart("character", startPos);
|
||||
range.moveEnd("character", endPos);
|
||||
range.select();
|
||||
} else {
|
||||
// Firefox support
|
||||
textDom.setSelectionRange(startPos, endPos);
|
||||
textDom.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在光标后插入文本
|
||||
* 参数:
|
||||
* textDom [JavaScript DOM String] 当前对象
|
||||
* value [String] 要插入的文本
|
||||
*/
|
||||
function insertAfterText(textDom, value) {
|
||||
var selectRange;
|
||||
if (document.selection) {
|
||||
// IE Support
|
||||
textDom.focus();
|
||||
selectRange = document.selection.createRange();
|
||||
selectRange.text = value;
|
||||
textDom.focus();
|
||||
} else if (textDom.selectionStart || textDom.selectionStart == '0') {
|
||||
// Firefox support
|
||||
var startPos = textDom.selectionStart;
|
||||
var endPos = textDom.selectionEnd;
|
||||
var scrollTop = textDom.scrollTop;
|
||||
textDom.value = textDom.value.substring(0, startPos) + value + textDom.value.substring(endPos, textDom.value.length);
|
||||
textDom.focus();
|
||||
textDom.selectionStart = startPos + value.length;
|
||||
textDom.selectionEnd = startPos + value.length;
|
||||
textDom.scrollTop = scrollTop;
|
||||
} else {
|
||||
textDom.value += value;
|
||||
textDom.focus();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getCursortPosition,
|
||||
setCaretPosition,
|
||||
getSelectText,
|
||||
setSelectText,
|
||||
insertAfterText
|
||||
};
|
30
src/markdown/js/tools.js
Normal file
30
src/markdown/js/tools.js
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 默认头部菜单配置
|
||||
* */
|
||||
export default {
|
||||
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
|
||||
}
|
18
src/markdown/js/utils.js
Normal file
18
src/markdown/js/utils.js
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
/*
|
||||
* 保存文件到本地
|
||||
* */
|
||||
export function saveFile(fileData, name) {
|
||||
var pom = document.createElement('a');
|
||||
pom.setAttribute('href', 'data:text/plain;charset=UTF-8,' + encodeURIComponent(fileData));
|
||||
pom.setAttribute('download', name);
|
||||
pom.style.display = 'none';
|
||||
if (document.createEvent) {
|
||||
const event = document.createEvent('MouseEvents');
|
||||
event.initEvent('click', true, true);
|
||||
pom.dispatchEvent(event);
|
||||
} else {
|
||||
pom.click();
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,18 @@
|
||||
import hljs from 'highlight.js';
|
||||
import hljs from './js/hljs';
|
||||
import marked from 'marked';
|
||||
import range from './js/range';
|
||||
import Print from './js/print';
|
||||
import {saveFile} from "./js/utils";
|
||||
import defaultTools from './js/tools';
|
||||
|
||||
hljs.initHighlightingOnLoad();
|
||||
const renderer = new marked.Renderer();
|
||||
|
||||
marked.setOptions({
|
||||
renderer: new marked.Renderer(),
|
||||
renderer,
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: true,
|
||||
sanitize: false,
|
||||
smartLists: true,
|
||||
highlight: function (code) {
|
||||
return hljs.highlightAuto(code).value;
|
||||
@ -21,182 +22,155 @@ marked.setOptions({
|
||||
export default {
|
||||
name: 'markdown',
|
||||
props: {
|
||||
title: { // 标题
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
titleStyle: { // 标题样式
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
initialValue: String,// 初始化内容
|
||||
theme: { // 默认主题
|
||||
type: String,
|
||||
default: 'Light'
|
||||
},
|
||||
width: { // 宽度
|
||||
width: { // 初始化宽度
|
||||
type: [Number, String],
|
||||
default: 'auto'
|
||||
},
|
||||
height: { // 高度
|
||||
height: { // 初始化高度
|
||||
type: Number,
|
||||
default: 600
|
||||
}, // 宽度
|
||||
toolbars: { // 工具栏
|
||||
type: Object,
|
||||
default () {
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
autoSave: { // 是否自动保存
|
||||
autoSave: {// 是否自动保存
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
interval: { // 自动保存频率 单位:毫秒
|
||||
interval: {// 自动保存间隔 mm
|
||||
type: Number,
|
||||
default: 10000
|
||||
default: 100000
|
||||
},
|
||||
initialValue: { // 初始化值
|
||||
exportFileName: {// 默认导出文件名称
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
mode: { // 模式 1 分屏显示 2 预览详情 3 全屏编辑
|
||||
type: [Number, String],
|
||||
default: 1
|
||||
default: '未命名文件'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: '', // 输入框内容
|
||||
timeoutId: null,
|
||||
hljsInit: null,
|
||||
indexLenth: 1,
|
||||
previewMarkdown: '',
|
||||
indexLenth: 100,
|
||||
html: '',
|
||||
preview: 1, // 是否是预览状态
|
||||
isFullscreen: false,
|
||||
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
|
||||
|
||||
themeSlideDown: false,
|
||||
imgs: [],
|
||||
scrolling: true,// 同步滚动
|
||||
editorHeight: this.height,
|
||||
editorWidth: this.width,
|
||||
previewImgModal: false,
|
||||
previewImgSrc: '',
|
||||
previewImgMode: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
editorHeight() {
|
||||
if (this.isFullscreen) {
|
||||
return window.innerHeight;
|
||||
} else {
|
||||
return this.height;
|
||||
}
|
||||
},
|
||||
tools() {
|
||||
const {
|
||||
allTools,
|
||||
toolbars
|
||||
} = this;
|
||||
return Object.assign(allTools, toolbars)
|
||||
const {toolbars = {}} = this;
|
||||
return {
|
||||
...defaultTools,
|
||||
...toolbars
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$refs.textarea.focus();
|
||||
})
|
||||
this.init();
|
||||
//this.addListener();
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.themeName = this.theme;
|
||||
const {
|
||||
autoSave,
|
||||
interval,
|
||||
theme,
|
||||
initialValue,
|
||||
mode
|
||||
} = this;
|
||||
this.value = initialValue;
|
||||
this.preview = mode;
|
||||
this.previewMarkdown = marked(initialValue, {
|
||||
sanitize: true
|
||||
});
|
||||
if (autoSave) {
|
||||
setTimeout(() => {
|
||||
this.value = this.initialValue;
|
||||
const textarea = this.$refs.textarea;
|
||||
textarea.focus();
|
||||
textarea.addEventListener('keydown', e => {
|
||||
if (e.keyCode === 83) {
|
||||
if (e.metaKey || e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
this.handleSave();
|
||||
}
|
||||
}
|
||||
})
|
||||
textarea.addEventListener('paste', this.handlePaste);
|
||||
if (this.autoSave) {
|
||||
this.timerId = setInterval(() => {
|
||||
this.handleSave();
|
||||
}, interval)
|
||||
}, this.interval);
|
||||
}
|
||||
}, 20)
|
||||
},
|
||||
methods: {
|
||||
handlePaste(e) { // 粘贴图片
|
||||
const {clipboardData = {}} = e;
|
||||
const {types = [], items} = clipboardData;
|
||||
let item = null;
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
if (types[i] === 'Files') {
|
||||
item = items[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (item) {
|
||||
const file = item.getAsFile();
|
||||
if (/image/ig.test(file.type)) {
|
||||
this.$emit('on-paste-image', file);
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
markdownScroll() {
|
||||
const {scrolling} = this;
|
||||
if (!scrolling) {
|
||||
return;
|
||||
}
|
||||
if (this.scroll === 'markdown') {
|
||||
const markdownContent = this.$refs.markdownContent;
|
||||
const preview = this.$refs.preview;
|
||||
const markdownScrollHeight = markdownContent.scrollHeight;
|
||||
const markdownScrollTop = markdownContent.scrollTop;
|
||||
const previewScrollHeight = preview.scrollHeight;
|
||||
preview.scrollTop = parseInt((markdownScrollTop / markdownScrollHeight) * previewScrollHeight);
|
||||
preview.scrollTop = parseInt(markdownScrollTop / markdownScrollHeight * previewScrollHeight, 0);
|
||||
}
|
||||
},
|
||||
previewScroll() {
|
||||
const {scrolling} = this;
|
||||
if (!scrolling) {
|
||||
return;
|
||||
}
|
||||
if (this.scroll === 'preview') {
|
||||
const markdownContent = this.$refs.markdownContent;
|
||||
const preview = this.$refs.preview;
|
||||
const markdownScrollHeight = markdownContent.scrollHeight;
|
||||
const previewScrollHeight = preview.scrollHeight;
|
||||
const previewScrollTop = preview.scrollTop;
|
||||
markdownContent.scrollTop = parseInt((previewScrollTop / previewScrollHeight) * markdownScrollHeight);
|
||||
markdownContent.scrollTop = parseInt(previewScrollTop / previewScrollHeight * markdownScrollHeight, 0);
|
||||
}
|
||||
},
|
||||
mousescrollSide(side) { // 设置究竟是哪个半边在主动滑动
|
||||
this.scroll = side;
|
||||
},
|
||||
insertContent(str) { // 插入文本
|
||||
const {
|
||||
preview
|
||||
} = this;
|
||||
insertContent(initStr) { // 插入文本
|
||||
const {preview} = this;
|
||||
if (preview === 2) {
|
||||
return;
|
||||
}
|
||||
this.lastInsert = str;
|
||||
const textareaDom = this.$refs.textarea;
|
||||
this.lastInsert = initStr;
|
||||
const point = this.getCursortPosition();
|
||||
const lastChart = this.value.substring(point - 1, point);
|
||||
const lastFourCharts = this.value.substring(point - 4, point);
|
||||
if (lastChart != '\n' && this.value != '' && lastFourCharts != ' ') {
|
||||
str = '\n' + str;
|
||||
if (lastChart !== '\n' && this.value !== '' && lastFourCharts !== ' ') {
|
||||
const str = '\n' + initStr;
|
||||
this.insertAfterText(str);
|
||||
} else {
|
||||
this.insertAfterText(str);
|
||||
this.insertAfterText(initStr);
|
||||
}
|
||||
},
|
||||
getCursortPosition() { // 获取光标位置
|
||||
@ -207,7 +181,7 @@ export default {
|
||||
let selectRange = document.selection.createRange();
|
||||
selectRange.moveStart('character', -this.value.length);
|
||||
cursorPos = selectRange.text.length;
|
||||
} else if (textDom.selectionStart || textDom.selectionStart == '0') {
|
||||
} else if (textDom.selectionStart || parseInt(textDom.selectionStart, 0) === 0) {
|
||||
cursorPos = textDom.selectionStart;
|
||||
}
|
||||
return cursorPos;
|
||||
@ -220,7 +194,7 @@ export default {
|
||||
selectRange = document.selection.createRange();
|
||||
selectRange.text = value;
|
||||
textDom.focus();
|
||||
} else if (textDom.selectionStart || textDom.selectionStart == '0') {
|
||||
} else if (textDom.selectionStart || parseInt(textDom.selectionStart, 0) === 0) {
|
||||
const startPos = textDom.selectionStart;
|
||||
const endPos = textDom.selectionEnd;
|
||||
const scrollTop = textDom.scrollTop;
|
||||
@ -241,27 +215,13 @@ export default {
|
||||
textDom.focus();
|
||||
textDom.setSelectionRange(position, position);
|
||||
} else if (textDom.createTextRange) {
|
||||
var range = textDom.createTextRange();
|
||||
let range = textDom.createTextRange();
|
||||
range.collapse(true);
|
||||
range.moveEnd('character', position);
|
||||
range.moveStart('character', position);
|
||||
range.select();
|
||||
}
|
||||
},
|
||||
insertLine() { // 插入分割线
|
||||
this.insertContent(`\n----\n`);
|
||||
},
|
||||
insertTitle(level) { // 插入标题
|
||||
const titleLevel = {
|
||||
1: '\n# ',
|
||||
2: '\n## ',
|
||||
3: '\n### ',
|
||||
4: '\n#### ',
|
||||
5: '\n##### ',
|
||||
6: '\n###### '
|
||||
};
|
||||
this.insertContent(titleLevel[level]);
|
||||
},
|
||||
insertQuote() { // 引用
|
||||
this.insertContent('\n> ');
|
||||
},
|
||||
@ -278,75 +238,69 @@ export default {
|
||||
this.insertContent('- [ ] ');
|
||||
},
|
||||
insertLink() { // 插入链接
|
||||
this.insertContent('\n[插入链接](https://github.com/coinsuper)');
|
||||
this.insertContent('\n[插入链接](href)');
|
||||
},
|
||||
insertImage() { // 插入图片
|
||||
this.insertContent('\n![image](https://noticejs.oss-cn-hangzhou.aliyuncs.com/%E6%9C%AA%E6%A0%87%E9%A2%98-3.jpg)');
|
||||
this.insertContent('\n![image](imgUrl)');
|
||||
},
|
||||
insertTable() { // 插入表格
|
||||
this.insertContent(`\nheader 1 | header 2\n---|---\nrow 1 col 1 | row 1 col 2\nrow 2 col 1 | row 2 col 2\n\n`);
|
||||
this.insertContent('\nheader 1 | header 2\n---|---\nrow 1 col 1 | row 1 col 2\nrow 2 col 1 | row 2 col 2\n\n');
|
||||
},
|
||||
insertCode() { // 插入code
|
||||
const textareaDom = this.$refs.textarea;
|
||||
const point = this.getCursortPosition();
|
||||
const lastChart = this.value.substring(point - 1, point);
|
||||
this.insertContent('\n```\n\n```');
|
||||
if (lastChart != '\n' && this.value != '') {
|
||||
if (lastChart !== '\n' && this.value !== '') {
|
||||
this.setCaretPosition(point + 5);
|
||||
} else {
|
||||
this.setCaretPosition(point + 5);
|
||||
}
|
||||
},
|
||||
insertStrong() { // 粗体
|
||||
const textareaDom = this.$refs.textarea;
|
||||
const point = this.getCursortPosition();
|
||||
const lastChart = this.value.substring(point - 1, point);
|
||||
this.insertContent('****');
|
||||
if (lastChart != '\n' && this.value != '') {
|
||||
if (lastChart !== '\n' && this.value !== '') {
|
||||
this.setCaretPosition(point + 2);
|
||||
} else {
|
||||
this.setCaretPosition(point + 2);
|
||||
}
|
||||
},
|
||||
insertItalic() { // 斜体
|
||||
const textareaDom = this.$refs.textarea;
|
||||
const point = this.getCursortPosition();
|
||||
const lastChart = this.value.substring(point - 1, point);
|
||||
this.insertContent('**');
|
||||
if (lastChart != '\n' && this.value != '') {
|
||||
if (lastChart !== '\n' && this.value !== '') {
|
||||
this.setCaretPosition(point + 1);
|
||||
} else {
|
||||
this.setCaretPosition(point + 1);
|
||||
}
|
||||
},
|
||||
insertBg() { // 背景色
|
||||
const textareaDom = this.$refs.textarea;
|
||||
const point = this.getCursortPosition();
|
||||
const lastChart = this.value.substring(point - 1, point);
|
||||
this.insertContent('====');
|
||||
if (lastChart != '\n' && this.value != '') {
|
||||
if (lastChart !== '\n' && this.value !== '') {
|
||||
this.setCaretPosition(point + 5);
|
||||
} else {
|
||||
this.setCaretPosition(point + 5);
|
||||
}
|
||||
},
|
||||
insertUnderline() { // 下划线
|
||||
const textareaDom = this.$refs.textarea;
|
||||
const point = this.getCursortPosition();
|
||||
const lastChart = this.value.substring(point - 1, point);
|
||||
this.insertContent('<u></u>');
|
||||
if (lastChart != '\n' && this.value != '') {
|
||||
if (lastChart !== '\n' && this.value !== '') {
|
||||
this.setCaretPosition(point + 3);
|
||||
} else {
|
||||
this.setCaretPosition(point + 5);
|
||||
}
|
||||
},
|
||||
insertOverline() { // overline
|
||||
const textareaDom = this.$refs.textarea;
|
||||
const point = this.getCursortPosition();
|
||||
const lastChart = this.value.substring(point - 1, point);
|
||||
this.insertContent('~~~~');
|
||||
if (lastChart != '\n' && this.value != '') {
|
||||
if (lastChart !== '\n' && this.value !== '') {
|
||||
this.setCaretPosition(point + 2);
|
||||
} else {
|
||||
this.setCaretPosition(point + 2);
|
||||
@ -363,10 +317,6 @@ export default {
|
||||
};
|
||||
this.insertContent(titleLevel[level]);
|
||||
},
|
||||
save(e) { // ctrl+s 保存
|
||||
e.preventDefault();
|
||||
this.handleSave();
|
||||
},
|
||||
tab(e) { // 屏蔽teatarea tab默认事件
|
||||
this.insertContent(' ', this);
|
||||
if (e.preventDefault) {
|
||||
@ -376,26 +326,25 @@ export default {
|
||||
}
|
||||
},
|
||||
handleSave() { // 保存操作
|
||||
const {value, html, themeName} = this;
|
||||
this.$emit('on-save', {
|
||||
markdownValue: this.value,
|
||||
htmlValue: this.previewMarkdown,
|
||||
theme: this.theme
|
||||
theme: themeName,
|
||||
value,
|
||||
html
|
||||
});
|
||||
},
|
||||
insertLine() { // 插入分割线
|
||||
this.insertContent(`\n----\n`);
|
||||
this.insertContent('\n----\n');
|
||||
},
|
||||
toggleSlideDown() { // 显示主题选项
|
||||
this.slideDown = !this.slideDown;
|
||||
},
|
||||
setThemes(name) { // 设置主题
|
||||
this.themeName = name;
|
||||
this.slideDown = false;
|
||||
this.themeSlideDown = false;
|
||||
},
|
||||
enter(e) { // 回车事件
|
||||
const {
|
||||
lastInsert
|
||||
} = this;
|
||||
enter() { // 回车事件
|
||||
const {lastInsert} = this;
|
||||
const list = ['- ', '1. ', '- [ ] ', '- [x] ']
|
||||
if (list.includes(lastInsert)) {
|
||||
this.insertContent(lastInsert);
|
||||
@ -403,84 +352,81 @@ export default {
|
||||
},
|
||||
onDelete() { // 删除时,以回车为界分割,如果数组最后一个元素为''时,将行一次插入的共嗯那个置为空,避免回车时再次插入
|
||||
const lines = this.value.split('\n');
|
||||
if (lines[lines.length - 1] == '') {
|
||||
if (lines[lines.length - 1] === '') {
|
||||
this.lastInsert = '';
|
||||
}
|
||||
},
|
||||
print() { // 打印文件
|
||||
const dom = this.$refs.preview;
|
||||
Print(dom);
|
||||
exportMd() { // 导出为.md格式
|
||||
saveFile(this.value, this.exportFileName + '.md');
|
||||
},
|
||||
addListener() { // 事件监听,阻止保存
|
||||
this.removeListener();
|
||||
document.addEventListener('keydown', this.listener)
|
||||
},
|
||||
removeListener() {
|
||||
document.removeEventListener('keydown', this.listener)
|
||||
},
|
||||
listener(e) {
|
||||
if (e.keyCode === 83) {
|
||||
if (e.metaKey || e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
this.handleSave();
|
||||
}
|
||||
}
|
||||
},
|
||||
importFile(e) { // 导入文件
|
||||
importFile(e) {// 导入本地文件
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
type
|
||||
} = file;
|
||||
const {type} = file;
|
||||
if (type !== 'text/markdown') {
|
||||
alert('文件格式有误');
|
||||
this.$Notice.error('文件格式有误!');
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(file, {
|
||||
encoding: 'utf-8'
|
||||
});
|
||||
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();
|
||||
addImageClickLintener() { // 监听查看大图
|
||||
const {imgs} = this;
|
||||
if (imgs.length > 0) {
|
||||
for (let i = 0, len = imgs.length; i < len; i++) {
|
||||
imgs[i].onclick = null;
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.imgs = this.$refs.preview.querySelectorAll('img');
|
||||
for (let i = 0, len = this.imgs.length; i < len; i++) {
|
||||
this.imgs[i].onclick = () => {
|
||||
const src = this.imgs[i].getAttribute('src');
|
||||
this.previewImage(src);
|
||||
}
|
||||
}
|
||||
}, 600);
|
||||
},
|
||||
previewImage(src) {// 预览图片
|
||||
const img = new Image();
|
||||
img.src = src;
|
||||
img.onload = () => {
|
||||
const width = img.naturalWidth;
|
||||
const height = img.naturalHeight;
|
||||
if ((height / width) > 1.4) {
|
||||
this.previewImgMode = 'horizontal';
|
||||
} else {
|
||||
this.previewImgMode = 'vertical';
|
||||
}
|
||||
this.previewImgSrc = src;
|
||||
this.previewImgModal = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
initialValue() {
|
||||
this.value = this.initialValue;
|
||||
},
|
||||
value() {
|
||||
clearTimeout(this.timeoutId);
|
||||
this.timeoutId = setTimeout(() => {
|
||||
this.previewMarkdown = marked(this.value, {
|
||||
sanitize: true
|
||||
this.html = marked(this.value, {
|
||||
sanitize: false
|
||||
});
|
||||
}, 30)
|
||||
this.indexLenth = this.value.split('\n').length;
|
||||
const height_1 = this.indexLenth * 22;
|
||||
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;
|
||||
const height1 = this.indexLenth * 22;
|
||||
const height2 = this.$refs.textarea.scrollHeight;
|
||||
const height3 = this.$refs.preview.scrollHeight;
|
||||
this.scrollHeight = Math.max(height1, height2, height3);
|
||||
this.indexLenth = parseInt(this.scrollHeight / 22, 0) - 1;
|
||||
this.addImageClickLintener();
|
||||
}
|
||||
},
|
||||
destroyed() { // 销毁时清除定时器
|
||||
|
@ -1,6 +1,7 @@
|
||||
var path = require('path')
|
||||
var webpack = require('webpack')
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
const CompressionWebpackPlugin = require('compression-webpack-plugin')
|
||||
|
||||
module.exports = {
|
||||
entry: NODE_ENV==='npm'?'./src/index.js':'./src/main.js',
|
||||
@ -12,6 +13,7 @@ module.exports = {
|
||||
library: 'markdown-vue',
|
||||
umdNamedDefine: true
|
||||
},
|
||||
devtool: NODE_ENV==='develop'?'cheap-module-eval-source-map':'cheap-module-source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
@ -60,7 +62,7 @@ module.exports = {
|
||||
devtool: '#eval-source-map'
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
if (NODE_ENV === 'production'||NODE_ENV==='npm') {
|
||||
module.exports.devtool = '#source-map'
|
||||
// http://vue-loader.vuejs.org/en/workflow/production.html
|
||||
module.exports.plugins = (module.exports.plugins || []).concat([
|
||||
@ -77,6 +79,9 @@ if (process.env.NODE_ENV === 'production') {
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true
|
||||
})
|
||||
}),
|
||||
new CompressionWebpackPlugin({
|
||||
algorithm: 'gzip'
|
||||
})
|
||||
])
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user