├── .babelrc
├── .editorconfig
├── .github
└── FUNDING.yml
├── .npmignore
├── CNAME
├── LICENSE
├── README.md
├── dist
├── css
│ ├── main.css
│ └── main.css.map
└── vue-bl-mark-down-editor.js
├── html
├── index.html
└── main.22e52bb.js
├── package.json
├── src
├── MarkDownEditor.vue
├── core
│ └── hljs
│ │ └── lang.hljs.js
├── dev
│ ├── App.vue
│ ├── index.html
│ ├── main.js
│ ├── toolBar
│ │ ├── Audio.vue
│ │ ├── Example1.vue
│ │ ├── Example2.vue
│ │ ├── MyCanvas.vue
│ │ ├── SlotExample1.vue
│ │ ├── SlotExample2.vue
│ │ └── 有更多好的工具栏可以直接提交请求哦
│ └── utils
│ │ ├── HZRecorder.js
│ │ └── axios.js
├── index.js
├── lib
│ ├── Markdown.js
│ └── MarkdownFunction.js
└── toolBar
│ ├── about.vue
│ ├── align-center.vue
│ ├── align-left.vue
│ ├── align-right.vue
│ ├── bold.vue
│ ├── code.vue
│ ├── emoji.vue
│ ├── header.vue
│ ├── italic.vue
│ ├── link.vue
│ ├── list-ol.vue
│ ├── list-ul.vue
│ ├── picture.vue
│ ├── quote-left.vue
│ ├── repeat.vue
│ ├── separator.vue
│ ├── strikethrough.vue
│ ├── subscript.vue
│ ├── superscript.vue
│ ├── table.vue
│ ├── thumb-tack.vue
│ ├── trash.vue
│ ├── underline.vue
│ └── undo.vue
├── webpack.build.config.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", { "modules": false }],
4 | "stage-2"
5 | ],
6 | "plugins": [
7 | "transform-vue-jsx",
8 | "transform-runtime"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 |
2 | custom: [
3 | 'http://paypal.me/blowsnow',
4 | 'https://i.loli.net/2019/09/02/vrJgXxpKmnHBLyD.png'
5 | ]
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .*
2 | build/
3 | node_modules/
4 | src/
5 | test/
6 | config/
7 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | markdown-editor.bload.cn
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 blowsnowit
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | @[toc](目录)
2 |
3 | # vue-bl-markdown-editor
4 |
5 | > 一个基于markdown-it 高度可扩展的vue编辑器组件
6 |
7 | > 允许提供插槽自定义工具栏
8 |
9 | > 允许通过动态注册组件,允许插入工具栏指定位置
10 |
11 | > 优势: 可完全自定义工具栏功能,markdown-it插件调用等
12 |
13 | > [演示站](http://markdown-editor.bload.cn/html/)
14 |
15 | 
16 |
17 | ### 安装
18 | ```
19 | $ npm install vue-bl-markdown-editor --save
20 | ```
21 | ### Use (如何引入)
22 |
23 | `main.js`:
24 | ```javascript
25 | // 全局注册
26 | // import with ES6
27 | import Vue from 'vue'
28 | import MarkDownEditor from 'vue-bl-markdown-editor'
29 | import 'vue-bl-markdown-editor/dist/css/main.css'
30 | // use
31 | Vue.use(MarkDownEditor);
32 | new Vue({
33 | 'el': '#main',
34 | data() {
35 | return { value: '' }
36 | }
37 | })
38 | ```
39 | ### 配置要求
40 | ```html
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | ```
49 |
50 |
51 | ### 配置
52 | |名称|类型|默认值|描述|
53 | |---|---|---|---|
54 | |placeholder|String|请输入内容|提示文本|
55 | |height|Number|500|编辑器高度|
56 | |isShowToolBar|Boolean|true|是否显示工具栏|
57 | |isShowToolBarRight|Boolean|true|是否显示右侧工具栏|
58 | |showMode|String|edit|显示模式,edit,see 编辑/预览模式,isSplit true下无效|
59 | |isShowSplit|Boolean|true|是否分屏,手机只可显示一个,此状态无效|
60 | |toolBars|Array|见下面|工具栏,排序和显示|
61 | |config|Object|见下面|工具栏的配置|
62 | |isSyncScroll|Boolean|true|是否同步滚动|
63 |
64 |
65 |
66 | #### 工具栏
67 | > 开发工具栏例子查看 src/dev/toolBar
68 |
69 | |名称|描述|
70 | |---|---|
71 | |bold|粗体|
72 | |italic|斜体|
73 | |header|标题|
74 | |underline|下划线|
75 | |strikethrough|中划线|
76 | |thumb-tack|标记|
77 | |superscript|上角标|
78 | |subscript|下角标|
79 | |align-left|居左|
80 | |align-center|居中|
81 | |align-right|居右|
82 | |quote-left|段落引用|
83 | |list-ol|有序列表|
84 | |list-ul|无须列表|
85 | |link|链接|
86 | |picture|图片|
87 | |code|代码块|
88 | |table|表格|
89 | |emoji|表情|
90 | |undo|上一步|
91 | |repeat|下一步|
92 | |trash|清空|
93 | |about|关于,希望保留|
94 | |separator|分隔符|
95 | #### 工具栏配置
96 | ```javascript
97 | config: {
98 | emojis:{
99 | // 配置多个表情
100 | more:[{name:'test',datas:['1','2']}],
101 | //是否覆盖默认的
102 | isCover: true
103 | },
104 | // 配置图片上传
105 | picture:{
106 | // 需要传回去上传后的路径
107 | // from paste/drag/upload 粘贴/拖拽/上传
108 | // 回调 异步请使用promise 案例看 src/dev/App.vue
109 | uploadCallback: (file,from)=>{
110 | return new Promise(resolve => {
111 | resolve({name:'',url: ''});
112 | });
113 | },
114 | // 是否解析图片列表
115 | resolving: true
116 | }
117 | }
118 | toolBars:[
119 | 'bold','italic',...更多
120 | ]
121 | ```
122 |
123 | #### 自定义工具栏
124 | ```javascript
125 | let editor = this.$refs.editor;
126 | let toolBar1 = editor.registerToolBarComponent('demo1',require(Example1.vue));
127 | editor.addToolBar(toolBar1/*,0 插入位置*/);
128 | ```
129 |
130 | ### 方法
131 | |名称|参数|描述|
132 | |---|---|---|
133 | |registerToolBarComponent|组件名,组件|动态注册组件作为工具栏,使用方法看上|
134 | |addToolBar|registerToolBarComponent返回的实例化组件,添加位置(默认最后)|添加工具栏组件|
135 | |delToolBar|删除位置|删除指定位置工具栏(不包括通过插槽加入的)|
136 | |insertContent|前缀,内容,后缀,是否强制替换内容,是否插入的时候选择|插入内容|
137 |
138 | ### 事件
139 | |名称|参数|描述|
140 | |---|---|---|
141 | |input|内容|输入内容|
142 | |ready|markdownit|加载完毕|
143 |
144 | ### 插槽
145 | |名称|描述|
146 | |---|---|
147 | |tool-bar-left-head|工具栏左侧头部插槽|
148 | |tool-bar-left-foot|工具栏左侧尾部插槽|
149 | |tool-bar-right-head|工具栏右侧头部插槽|
150 | |tool-bar-right-foot|工具栏右侧尾部插槽|
151 |
152 | ### 高级扩展
153 | - 允许自行调用markdownit 注册插件 (从ready事件中获取/直接从ref中获取)
154 |
155 |
156 | ## TODO
157 | - [X] 基础工具栏
158 | - [X] 实现撤销恢复功能
159 | - [X] 兼容手机
160 | - [X] 样式美化
161 | - [X] 本项目中打包dev演示页面
162 | - [X] highlight 样式引用(*)
163 | - [X] 图片上传回调配置
164 | - [X] 图片粘贴上传
165 | - [x] 图片拖拽上传
166 | - [x] 同步滚动(暂时按滚动条高度计算**)
167 | > 录音兼容问题(测试chrom/firefox正常,edge申请失败)
168 |
169 | > 必须运行在 https 下 测试可以正常运行
170 | - [x] 自定义录音工具栏组件(*)
171 | - [ ] 本地图片粘贴上传不支持(待解决)
172 | ## 测试
173 | - [x] 自定义工具栏
174 | - [x] 表情配置
175 | - [X] 图片上传
176 | - [X] 粘贴上传
177 | - [x] 拖入上传
178 |
179 | ## BUG
180 | - [X] 最底部插入列表无法自动换行到下一行显示(使用回车自动向下滚动)
181 | - [ ] 手机模式下工具栏最后一栏位置异常(pc模拟手机 还原不了无法测试)
182 | - [ ] 实时渲染 太卡导致连输,不正常显示
183 | #### 希望大家一起开发好用的工具栏吧
184 |
185 | ## 赞助
186 | 
187 |
--------------------------------------------------------------------------------
/dist/css/main.css:
--------------------------------------------------------------------------------
1 | .mark-down-editor .tool-bar{box-shadow:0 0 3px rgba(0,0,0,.157),0 0 3px rgba(0,0,0,.227);display:flex;flex-wrap:wrap;align-items:center;background-color:#fff;border:1px solid #ccc;padding:0 10px;line-height:35px}.mark-down-editor .tool-bar>div{height:35px;margin-right:10px;cursor:pointer}.mark-down-editor .tool-bar .tool-right>span{margin-right:10px}.mark-down-editor .tool-bar>div .mark-down-name{position:relative}.mark-down-editor .tool-bar>div .mark-down-name:hover>.tool-bar-box{display:block}.mark-down-editor .tool-bar>div .tool-bar-box{position:absolute;background:#fff;min-width:130px;z-index:1600;box-shadow:0 0 4px rgba(0,0,0,.157),0 0 4px rgba(0,0,0,.227);-webkit-transition:all .2s linear 0s;transition:all .2s linear 0s;display:none;left:-50px;border-radius:5px}.mark-down-editor .tool-bar>div .mark-down-form-box{padding:10px}.mark-down-editor .tool-bar>div .mark-down-form-box>div{margin:10px 0}.mark-down-editor .tool-bar>div .mark-down-dropdown>ul{list-style:none;padding:0;margin:0}.mark-down-editor .tool-bar>div .mark-down-dropdown>ul>li{text-align:center;height:35px;line-height:35px;font-size:12px;-webkit-transition:all .2s linear 0s;transition:all .2s linear 0s;position:relative}.mark-down-editor .tool-bar>div .mark-down-dropdown>ul>li:hover{background:#eaeaea}.mark-down-editor{display:flex;flex-direction:column}.mark-down-editor .container{display:flex;height:100%}.mark-down-editor .container .middle{width:10px;cursor:col-resize;font-size:16px;display:flex;align-items:center;justify-content:center;background:#fff}.mark-down-editor .container.container-column{flex-direction:column}.mark-down-editor .container .box{box-shadow:0 0 3px rgba(0,0,0,.157),0 0 3px rgba(0,0,0,.227);width:50%;height:100%;background:#fff;position:relative;overflow-y:auto;overflow-x:hidden}.mark-down-editor .container .box .box-padding{padding:10px;height:94%}.mark-down-editor .container .box.markdown-body{overflow:auto}.mark-down-editor .container .editor{width:100%;height:100%;outline:0 none;border:none!important;resize:none;position:absolute;top:0;left:0;overflow:hidden}.mark-down-editor .container .editor,.mark-down-editor .container .editor-pre{font-size:15px;line-height:1.5;font-family:Menlo,Ubuntu Mono,Consolas,Courier New,Microsoft Yahei,Hiragino Sans GB,WenQuanYi Micro Hei,sans-serif}.mark-down-editor .container .editor-pre{visibility:hidden;white-space:pre-wrap;word-wrap:break-word;margin:0}.markdown-body .hljs-left{text-align:left}.markdown-body .hljs-center{text-align:center}.markdown-body .hljs-right{text-align:right}.mark-down-editor ::-webkit-scrollbar-track-piece{background-color:#e5e5e5}.mark-down-editor ::-webkit-scrollbar{width:8px;height:9px}.mark-down-editor ::-webkit-scrollbar-thumb{background-color:#b7b7b7;background-clip:padding-box;min-height:50px;border-radius:20%}.mark-down-editor ::-webkit-scrollbar-thumb:hover{background-color:#a1a1a1}.about .mark-down-form-box{width:250px;text-align:center}.about .mark-down-form-box p{margin:0}.emoji .tool-bar-box{width:300px}.emoji .emoji-name-list>span{color:#9abbc8;padding:5px}.emoji .emoji-name-list>span.active{background:#f0f0f0}.emoji .emoji-list{display:flex;flex-wrap:wrap}.emoji .emoji-list>span{text-align:center;padding:4px 2px;margin:-1px 0 0 -1px;border:1px solid #e8e8e8}.emoji .emoji-list>span:hover{border:1px solid #0095cd;z-index:2}.table .unhighlighted{position:relative;height:220px;width:220px;background:url(data:image/gif;base64,R0lGODlhFgAWAKECAPj4+Onp6f///////yH5BAEKAAIALAAAAAAWABYAAAJAlI+pFu0P3wmg2otBm7nbzXgeKFDAiaYqaaouyr6yFnCzG99rHepp7jsBg0NfUXe8JWdLGSsChUyiVN7iis1mCwA7) repeat!important}.table .highlighted{position:absolute;max-height:220px;max-width:220px;background:url(data:image/gif;base64,R0lGODlhFgAWAKECAN3q+8PZ/////////yH5BAEKAAIALAAAAAAWABYAAAJAlI+pFu0P3wmg2otBm7nbzXgeKFDAiaYqaaouyr6yFnCzG99rHepp7jsBg0NfUXe8JWdLGSsChUyiVN7iis1mCwA7) repeat!important}.code .mark-down-form-box{width:400px}.link .mark-down-dropdown{flex-direction:column}.header{position:relative}body{margin:0}#app{background:#9bb9eaf5;background:linear-gradient(0deg,#ade8ff,#9bb9ea);height:100vh}.head-box{text-align:center;padding:10px;color:#fff}.mark-down-editor{height:70vh;width:90%;margin:0 auto;padding:20px}.mark-down-editor .foot{box-shadow:0 0 3px rgba(0,0,0,.157),0 0 3px rgba(0,0,0,.227);font-size:12px;display:flex;justify-content:space-between;align-items:center;background-color:#fff;border:1px solid #ccc;padding:0 10px;line-height:20px}.slot-example2 .mark-down-form-box{width:200px;text-align:center}.canvas .canvas-box{width:70vw;height:80vh;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);background:#fff;z-index:3;box-shadow:0 0 5px 1px #5a5a5a}.canvas .canvas-box canvas{width:100%;height:100%}.canvas .canvas-box .actions{position:absolute;left:20px}.canvas .canvas-box .colors{position:absolute;top:40px;left:20px;display:flex;flex-direction:column}.canvas .canvas-box .colors>span{border-radius:50%;height:20px;width:20px;margin:10px 0;transition:all .3s}.canvas .canvas-box .colors>span.active{box-shadow:0 0 3px rgba(0,0,0,.95);transform:scale(1.2)}.example2 .mark-down-form-box{width:200px;text-align:center}.markdown-body .audioplayer{height:20px}.markdown-body .audioplayer:focus{outline:none}
2 | /*# sourceMappingURL=main.css.map*/
--------------------------------------------------------------------------------
/dist/css/main.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"css/main.css","sourceRoot":""}
--------------------------------------------------------------------------------
/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | vue-markdown-editor
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-bl-markdown-editor",
3 | "description": "一个基于markdown-it高度可扩展的vue编辑器组件",
4 | "version": "2.0.0",
5 | "author": "blowsnow",
6 | "license": "MIT",
7 | "private": false,
8 | "main": "dist/vue-bl-mark-down-editor.js",
9 | "files": [
10 | "dist/*"
11 | ],
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/blowsnowit/vue-bl-markdown-editor.git"
15 | },
16 | "scripts": {
17 | "dev": "cross-env NODE_ENV=development webpack-dev-server --hot",
18 | "build-html": "cross-env NODE_ENV=production webpack --progress --hide-modules",
19 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules --config webpack.build.config.js"
20 | },
21 | "dependencies": {
22 | "highlight.js": "^9.11.0",
23 | "highlight.js-async-webpack": "^1.0.4"
24 | },
25 | "browserslist": [
26 | "> 1%",
27 | "last 2 versions",
28 | "not ie <= 8"
29 | ],
30 | "devDependencies": {
31 | "axios": "^0.19.0",
32 | "babel-core": "^6.22.1",
33 | "babel-helper-vue-jsx-merge-props": "^2.0.3",
34 | "babel-loader": "^7.1.1",
35 | "babel-plugin-syntax-jsx": "^6.18.0",
36 | "babel-plugin-transform-runtime": "^6.22.0",
37 | "babel-plugin-transform-vue-jsx": "^3.5.0",
38 | "babel-preset-env": "^1.3.2",
39 | "babel-preset-stage-2": "^6.22.0",
40 | "copy-webpack-plugin": "^4.0.1",
41 | "cross-env": "^5.0.5",
42 | "css-loader": "^0.28.7",
43 | "extract-text-webpack-plugin": "^2.1.0",
44 | "file-loader": "^1.1.4",
45 | "github-markdown-css": "^2.6.0",
46 | "html-webpack-plugin": "^2.28.0",
47 | "markdown-it": "^8.3.1",
48 | "markdown-it-abbr": "^1.0.4",
49 | "markdown-it-container": "^2.0.0",
50 | "markdown-it-deflist": "^2.0.0",
51 | "markdown-it-emoji": "^1.1.1",
52 | "markdown-it-footnote": "^3.0.1",
53 | "markdown-it-for-inline": "~0.1.0",
54 | "markdown-it-html5-embed": "^1.0.0",
55 | "markdown-it-images-preview": "^1.0.0",
56 | "markdown-it-ins": "^2.0.0",
57 | "markdown-it-katex-external": "^1.0.0",
58 | "markdown-it-mark": "^2.0.0",
59 | "markdown-it-sub": "^1.0.0",
60 | "markdown-it-sup": "^1.0.0",
61 | "markdown-it-task-lists": "^2.1.1",
62 | "markdown-it-toc": "^1.1.0",
63 | "merges-utils": "^1.0.2",
64 | "optimize-css-assets-webpack-plugin": "^1.3.1",
65 | "postcss-import": "^11.0.0",
66 | "postcss-loader": "^2.0.8",
67 | "postcss-url": "^7.2.1",
68 | "style-loader": "^1.0.0",
69 | "url-loader": "^0.5.8",
70 | "vue": "^2.5.11",
71 | "vue-loader": "^13.0.5",
72 | "vue-style-loader": "^3.0.1",
73 | "vue-template-compiler": "^2.4.4",
74 | "webpack": "^3.6.0",
75 | "webpack-bundle-analyzer": "^2.8.1",
76 | "webpack-dev-server": "^2.9.1",
77 | "webpack-md5-hash": "^0.0.5"
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/MarkDownEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
26 |
27 |
28 |
46 |
47 |
48 |
49 | ||
50 |
51 |
52 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
489 |
490 |
666 |
--------------------------------------------------------------------------------
/src/core/hljs/lang.hljs.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'sh': 'bash',
3 | 'c': 'cpp',
4 | 'c++': 'cpp',
5 | 'cs': 'cs',
6 | 'css': 'css',
7 | 'delphi': 'delphi',
8 | 'diff': 'diff',
9 | 'dos': 'dos',
10 | 'excel': 'excel',
11 | 'go': 'go',
12 | 'java': 'java',
13 | 'javascript': 'javascript',
14 | 'json': 'json',
15 | 'kotlin': 'kotlin',
16 | 'lua': 'lua',
17 | 'markdown': 'markdown',
18 | 'php': 'php',
19 | 'powershell': 'powershell',
20 | 'python': 'python',
21 | 'ruby': 'ruby',
22 | 'shell': 'shell',
23 | 'sql': 'sql',
24 | 'vbscript': 'vbscript',
25 | 'xml': 'xml',
26 | 'html': 'xml',
27 | 'yaml': 'yaml',
28 | };
29 |
--------------------------------------------------------------------------------
/src/dev/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
vue-bl-markdown-editor
5 |
开源在线 Markdown 编辑器
6 |

7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
292 |
293 |
325 |
--------------------------------------------------------------------------------
/src/dev/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | vue-markdown-editor
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/dev/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import MarkDownEditor from '../index.js'
4 |
5 |
6 | Vue.use(MarkDownEditor);
7 | new Vue({
8 | el: '#app',
9 | render: h => h(App)
10 | })
11 |
--------------------------------------------------------------------------------
/src/dev/toolBar/Audio.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
58 |
59 |
70 |
--------------------------------------------------------------------------------
/src/dev/toolBar/Example1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ex1
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/dev/toolBar/Example2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Ex2
7 |
8 | 动态插入hover测试例子,点我试试
9 |
10 |
11 |
12 |
13 |
14 |
33 |
34 |
43 |
--------------------------------------------------------------------------------
/src/dev/toolBar/MyCanvas.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
can
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
92 |
93 |
135 |
--------------------------------------------------------------------------------
/src/dev/toolBar/SlotExample1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SEx1
6 |
7 |
8 |
9 |
28 |
29 |
34 |
--------------------------------------------------------------------------------
/src/dev/toolBar/SlotExample2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | SEx2
7 |
8 | 插槽hover测试例子,点我试试
9 |
10 |
11 |
12 |
13 |
14 |
35 |
36 |
45 |
--------------------------------------------------------------------------------
/src/dev/toolBar/有更多好的工具栏可以直接提交请求哦:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blowsnowit/vue-bl-markdown-editor/1e32771f00d2c0e084aeb7633027a810936b6cab/src/dev/toolBar/有更多好的工具栏可以直接提交请求哦
--------------------------------------------------------------------------------
/src/dev/utils/HZRecorder.js:
--------------------------------------------------------------------------------
1 | //兼容
2 | window.URL = window.URL || window.webkitURL;
3 | // navigator.mediaDevices.getUserMedia = navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
4 |
5 | let HZRecorder = function (stream, config) {
6 | config = config || {};
7 | config.sampleBits = config.sampleBits || 8; //采样数位 8, 16
8 | config.sampleRate = config.sampleRate || (44100 / 6); //采样率(1/6 44100)
9 |
10 | let context = new (window.webkitAudioContext || window.AudioContext)();
11 | let audioInput = context.createMediaStreamSource(stream);
12 | let createScript = context.createScriptProcessor || context.createJavaScriptNode;
13 | let recorder = createScript.apply(context, [4096, 1, 1]);
14 | let mediaRecorder = new MediaRecorder(stream);
15 | let blob = null;
16 | let onStop = null;
17 |
18 | let audioData = {
19 | inputSampleRate: context.sampleRate //输入采样率
20 | , inputSampleBits: 16 //输入采样数位 8, 16
21 | , outputSampleRate: config.sampleRate //输出采样率
22 | , oututSampleBits: config.sampleBits //输出采样数位 8, 16
23 | , compress: function (data) { //合并压缩 data 为 buffer
24 | //压缩
25 | let compression = parseInt(this.inputSampleRate / this.outputSampleRate);
26 | let length = data.length / compression;
27 | let result = new Float32Array(length);
28 | let index = 0, j = 0;
29 | while (index < length) {
30 | result[index] = data[j];
31 | j += compression;
32 | index++;
33 | }
34 | return result;
35 | }
36 | , encodeWAV: function (inBuffer) {
37 | let sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
38 | let sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
39 | let bytes = this.compress(inBuffer);
40 | let dataLength = bytes.length * (sampleBits / 8);
41 | let buffer = new ArrayBuffer(44 + dataLength);
42 | let data = new DataView(buffer);
43 | let channelCount = 1;//单声道
44 | let offset = 0;
45 |
46 | let writeString = function (str) {
47 | for (let i = 0; i < str.length; i++) {
48 | data.setUint8(offset + i, str.charCodeAt(i));
49 | }
50 | }
51 |
52 | // 资源交换文件标识符
53 | writeString('RIFF'); offset += 4;
54 | // 下个地址开始到文件尾总字节数,即文件大小-8
55 | data.setUint32(offset, 36 + dataLength, true); offset += 4;
56 | // WAV文件标志
57 | writeString('WAVE'); offset += 4;
58 | // 波形格式标志
59 | writeString('fmt '); offset += 4;
60 | // 过滤字节,一般为 0x10 = 16
61 | data.setUint32(offset, 16, true); offset += 4;
62 | // 格式类别 (PCM形式采样数据)
63 | data.setUint16(offset, 1, true); offset += 2;
64 | // 通道数
65 | data.setUint16(offset, channelCount, true); offset += 2;
66 | // 采样率,每秒样本数,表示每个通道的播放速度
67 | data.setUint32(offset, sampleRate, true); offset += 4;
68 | // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8
69 | data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;
70 | // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8
71 | data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
72 | // 每样本数据位数
73 | data.setUint16(offset, sampleBits, true); offset += 2;
74 | // 数据标识符
75 | writeString('data'); offset += 4;
76 | // 采样数据总数,即数据总大小-44
77 | data.setUint32(offset, dataLength, true); offset += 4;
78 | // 写入采样数据
79 | if (sampleBits === 8) {
80 | for (let i = 0; i < bytes.length; i++, offset++) {
81 | let s = Math.max(-1, Math.min(1, bytes[i]));
82 | let val = s < 0 ? s * 0x8000 : s * 0x7FFF;
83 | val = parseInt(255 / (65535 / (val + 32768)));
84 | data.setInt8(offset, val, true);
85 | }
86 | } else {
87 | for (let i = 0; i < bytes.length; i++, offset += 2) {
88 | let s = Math.max(-1, Math.min(1, bytes[i]));
89 | data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
90 | }
91 | }
92 |
93 | return new Blob([data], { type: 'audio/wav' });
94 | }
95 | };
96 |
97 |
98 | //开始录音
99 | this.start = function () {
100 | if (mediaRecorder.state === "recording") {
101 | mediaRecorder.stop();
102 | }
103 | mediaRecorder.start();
104 | console.log(mediaRecorder.state);
105 | }
106 |
107 | //停止
108 | this.stop = function () {
109 | mediaRecorder.stop();
110 | }
111 |
112 | //获取音频文件
113 | this.getBlob = function () {
114 | console.log(this.blob);
115 | return new Promise(resolve => {
116 | resolve(this.blob)
117 | // return;
118 | // //转换blob为 buffer
119 | // let reader = new FileReader();
120 | // //byte为blob对象
121 | // reader.readAsArrayBuffer(this.blob);
122 | // reader.onload= ()=>{
123 | // console.log(reader.result);
124 | // let buffer = new Uint8Array(reader.result);
125 | // console.log('转换数据',buffer);
126 | // resolve(audioData.encodeWAV(buffer));
127 | // }
128 | })
129 | }
130 |
131 | //回放
132 | this.play = function (audio) {
133 | audio.src = window.URL.createObjectURL(this.getBlob());
134 | }
135 |
136 | mediaRecorder.onstop = e=>{
137 | console.log('onstop ',e);
138 | this.getBlob().then((blob)=>{
139 | console.log('转换完毕数据',blob);
140 | this.onStop(blob);
141 | })
142 | }
143 |
144 | mediaRecorder.ondataavailable = e=>{
145 | this.blob = e.data;
146 | console.log('数据采集',this.blob);
147 | }
148 | //音频采集
149 | recorder.onaudioprocess = function (e) {
150 | audioData.input(e.inputBuffer.getChannelData(0));
151 | //record(e.inputBuffer.getChannelData(0));
152 | }
153 |
154 | };
155 | //抛出异常
156 | HZRecorder.throwError = function (message) {
157 | alert(message);
158 | throw new function () { this.toString = function () { return message; } }
159 | }
160 | //是否支持录音
161 | HZRecorder.canRecording = (navigator.mediaDevices && navigator.mediaDevices.getUserMedia != null);
162 | //获取录音机
163 | HZRecorder.get = function (callback, config) {
164 | if (callback) {
165 | if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
166 | navigator.mediaDevices.getUserMedia({ audio: true }).then(stream=>{
167 | let rec = new HZRecorder(stream, config);
168 | callback(rec);
169 | }).catch(error=>{
170 | console.log('申请',error);
171 | switch (error.code || error.name) {
172 | case 'PERMISSION_DENIED':
173 | case 'PermissionDeniedError':
174 | case 'NotAllowedError':
175 | HZRecorder.throwError('用户拒绝提供录音权限。');
176 | break;
177 | case 'NOT_SUPPORTED_ERROR':
178 | case 'NotSupportedError':
179 | HZRecorder.throwError('浏览器不支持硬件设备。');
180 | break;
181 | case 'MANDATORY_UNSATISFIED_ERROR':
182 | case 'MandatoryUnsatisfiedError':
183 | HZRecorder.throwError('无法发现指定的硬件设备。');
184 | break;
185 | default:
186 | HZRecorder.throwError('无法打开麦克风。异常信息:' + (error.code || error.name));
187 | break;
188 | }
189 | })
190 | } else {
191 | HZRecorder.throwError('当前浏览器不支持录音功能。'); return;
192 | }
193 | }
194 | }
195 |
196 |
197 |
198 |
199 |
200 | export default HZRecorder;
201 |
--------------------------------------------------------------------------------
/src/dev/utils/axios.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | // 是否是生产环境,日志只对非生成环境生效
4 | let noProduction = process.env.NODE_ENV !== 'production';
5 |
6 | axios.defaults.baseURL = ''; // 多环境地址
7 | axios.defaults.timeout = 45000; // 响应时间
8 | axios.defaults.withCredentials = true; // 允许跨域请求Cookie
9 | axios.defaults.headers['Content-Type'] = 'application/json';
10 | axios.defaults.headers['Accept'] = 'application/json';
11 |
12 | /**
13 | * 默认向外暴露一个请求对象,不建议全部import,尽量按需引入
14 | */
15 | export default {
16 |
17 | /**
18 | * post请求
19 | * @param options 请求数据对象
20 | * @returns {Promise}
21 | */
22 | requestPost(options) {
23 | return new Promise((resolve, reject) => {
24 | axios.post(options.api, options.param).then(response => {
25 | resolve(response.data);
26 | }, error => {
27 | reject(error);
28 | }).catch(throws => {
29 | if (noProduction) {
30 | console.log("requestPost.catch返回:", throws);
31 | }
32 | reject("网络异常!")
33 | })
34 | })
35 | },
36 |
37 | /**
38 | * get请求
39 | * @param options 请求数据对象
40 | * @returns {Promise}
41 | */
42 | requestGet(options) {
43 | return new Promise((resolve, reject) => {
44 | axios.get(options.api, {
45 | params: options.param
46 | }).then(response => {
47 | //请求成功
48 | resolve(response.data);
49 | }, error => {
50 | reject(error);
51 | }).catch(throws => {
52 | if (noProduction) {
53 | console.log("requestGet.catch返回:", throws);
54 | }
55 | reject("网络异常!")
56 | })
57 | })
58 | },
59 |
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const MarkDownEditor = require('./MarkDownEditor');
2 | const bold = require('./toolBar/bold.vue'); //粗体
3 | const italic = require('./toolBar/italic.vue'); //斜体
4 | const header = require('./toolBar/header.vue'); //标题
5 | const underline = require('./toolBar/underline.vue'); //下划线
6 | const strikethrough = require('./toolBar/strikethrough.vue'); //中划线
7 | const thumbTack = require('./toolBar/thumb-tack.vue'); //标记
8 | const superscript = require('./toolBar/superscript.vue'); //上角标
9 | const subscript = require('./toolBar/subscript.vue'); //下角标
10 | const alignLeft = require('./toolBar/align-left.vue'); //居左
11 | const alignCenter = require('./toolBar/align-center.vue'); //居中
12 | const alignRight = require('./toolBar/align-right.vue'); //居右
13 | const quoteLeft = require('./toolBar/quote-left.vue'); //段落引用
14 | const listOl = require('./toolBar/list-ol.vue'); //有序列表
15 | const listUl = require('./toolBar/list-ul.vue'); //无须列表
16 | const link = require('./toolBar/link.vue'); //链接
17 | const picture = require('./toolBar/picture.vue'); //图片
18 | const code = require('./toolBar/code.vue'); //代码块
19 | const table = require('./toolBar/table.vue'); //表格
20 | const emoji = require('./toolBar/emoji.vue');
21 | const undo = require('./toolBar/undo.vue'); //上一步
22 | const repeat = require('./toolBar/repeat.vue'); //下一步
23 | const trash = require('./toolBar/trash.vue'); //清空
24 | const about = require('./toolBar/about.vue'); //关于
25 |
26 |
27 | const install = function(Vue) {
28 | Vue.component('mark-down-editor', MarkDownEditor.default);
29 | };
30 |
31 | if (typeof window !== 'undefined' && window.Vue) {
32 | install(window.Vue);
33 | }
34 |
35 | const VueMarkDownEditor = {
36 | install: function(Vue) {
37 | Vue.component('mark-down-editor', MarkDownEditor.default);
38 | },
39 | MarkDownEditor,
40 | bold,
41 | italic,
42 | header,
43 | underline,
44 | strikethrough,
45 | thumbTack,
46 | superscript,
47 | subscript,
48 | alignLeft,
49 | alignCenter,
50 | alignRight,
51 | quoteLeft,
52 | listOl,
53 | listUl,
54 | link,
55 | picture,
56 | code,
57 | table,
58 | emoji,
59 | undo,
60 | repeat,
61 | trash,
62 | about,
63 | };
64 |
65 | module.exports = VueMarkDownEditor;
66 |
--------------------------------------------------------------------------------
/src/lib/Markdown.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 使用 markdown-it 解析
3 | * 默认功能:
4 | * 代码块(>),无序列表(-),标题(#)
5 | */
6 |
7 | const markdownIt = require('markdown-it');
8 | //region使用插件
9 | // 表情
10 | const emoji = require('markdown-it-emoji');
11 | // 下标
12 | const sub = require('markdown-it-sub')
13 | // 上标
14 | const sup = require('markdown-it-sup')
15 | //
16 | const deflist = require('markdown-it-deflist')
17 | //
18 | const abbr = require('markdown-it-abbr')
19 | // footnote
20 | const footnote = require('markdown-it-footnote')
21 | // insert 带有下划线 样式 ++ ++
22 | const insert = require('markdown-it-ins')
23 | // mark
24 | const mark = require('markdown-it-mark')
25 | // taskLists
26 | const taskLists = require('markdown-it-task-lists')
27 | // container 用于创建自定义的块级容器
28 | const container = require('markdown-it-container')
29 | //
30 | const toc = require('markdown-it-toc')
31 | // math katex
32 | const katex = require('markdown-it-katex-external');
33 | //图片预览
34 | const miip = require('markdown-it-images-preview');
35 |
36 | // const mhljs = require('markdown-it-highlightjs');
37 | //
38 | // const mihe = require('markdown-it-highlightjs-external');
39 | import hljsLangs from '../core/hljs/lang.hljs.js'
40 | const hljs = require('highlight.js');
41 | const missLangs = {};
42 | const needLangs = [];
43 | const hljs_opts = {
44 | hljs: 'auto',
45 | highlighted: true,
46 | langCheck: function(lang) {
47 | console.log(langCheck);
48 | if (lang && hljsLangs[lang] && !missLangs[lang]) {
49 | missLangs[lang] = 1;
50 | needLangs.push(hljsLangs[lang])
51 | }
52 | }
53 | };
54 | //endregion
55 | const defaultConfig = {
56 | html: true, // Enable HTML tags in source
57 | xhtmlOut: true, // Use '/' to close single tags (
).
58 | breaks: true, // Convert '\n' in paragraphs into
59 | langPrefix: 'lang-', // CSS language prefix for fenced blocks. Can be
60 | linkify: false, // 自动识别url
61 | typographer: true,
62 | quotes: '“”‘’',
63 | highlight: function (str, lang) {
64 | str = str.replace(/</g, "<");
65 | str = str.replace(/>/g, ">");
66 | // console.log('highlight',str, lang);
67 | if (lang && hljs.getLanguage(lang)) {
68 | try {
69 | return '' +
70 | hljs.highlight(lang, str, true).value +
71 | '
';
72 | } catch (__) {}
73 | }
74 |
75 | return '' + md.utils.escapeHtml(str) + '
';
76 | }
77 | }
78 | const md = new markdownIt(defaultConfig);
79 |
80 | const defaultRender = md.renderer.rules.link_open || function(tokens, idx, options, env, self) {
81 | return self.renderToken(tokens, idx, options);
82 | };
83 | md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
84 | // If you are sure other plugins can't add `target` - drop check below
85 | var aIndex = tokens[idx].attrIndex('target');
86 |
87 | if (aIndex < 0) {
88 | tokens[idx].attrPush(['target', '_blank']); // add new attribute
89 | } else {
90 | tokens[idx].attrs[aIndex][1] = '_blank'; // replace value of existing attr
91 | }
92 |
93 | // pass token to default renderer.
94 | return defaultRender(tokens, idx, options, env, self);
95 | };
96 |
97 |
98 | md.use(emoji)
99 | .use(sup)
100 | .use(sub)
101 | .use(container)
102 | .use(container, 'hljs-left') /* align left */
103 | .use(container, 'hljs-center')/* align center */
104 | .use(container, 'hljs-right')/* align right */
105 | .use(deflist)
106 | .use(abbr)
107 | .use(footnote)
108 | .use(insert)
109 | .use(mark)
110 | .use(miip)
111 | .use(katex)
112 | .use(taskLists)
113 | .use(toc)
114 | // .use(mhljs)
115 |
116 | export default md;
117 |
--------------------------------------------------------------------------------
/src/lib/MarkdownFunction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 滚动条联动
3 | */
4 | let mainFlag = false; // 抵消两个滚动事件之间互相触发
5 | let preFlag = false; // 如果两个 flag 都为 true,证明是反弹过来的事件引起的
6 | export const scrollLink = (event, who,editor,preview) => {
7 | let radio = (editor.scrollHeight - editor.clientHeight) / (preview.scrollHeight - preview.clientHeight);
8 | if(who == 'preview'){
9 | preFlag = true;
10 | if (mainFlag === true){ // 抵消两个滚动事件之间互相触发
11 | mainFlag = false;
12 | preFlag = false;
13 | return;
14 | }
15 | // console.log(who,radio,preview.scrollHeight,editor.scrollHeight);
16 | editor.scrollTop = Math.round(preview.scrollTop * radio);
17 | return;
18 | }
19 | if(who == 'editor'){
20 | mainFlag = true;
21 | if (preFlag === true){ // 抵消两个滚动事件之间互相触发
22 | mainFlag = false;
23 | preFlag = false;
24 | return;
25 | }
26 | // console.log(who,radio);
27 | preview.scrollTop = Math.round( editor.scrollTop / radio);
28 | return;
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/src/toolBar/about.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
20 |
31 |
32 |
44 |
--------------------------------------------------------------------------------
/src/toolBar/align-center.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/align-left.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/align-right.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/bold.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
24 |
25 |
30 |
--------------------------------------------------------------------------------
/src/toolBar/code.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
23 |
24 |
25 |
57 |
58 |
66 |
--------------------------------------------------------------------------------
/src/toolBar/emoji.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
68 |
69 |
101 |
--------------------------------------------------------------------------------
/src/toolBar/header.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
17 |
45 |
46 |
51 |
--------------------------------------------------------------------------------
/src/toolBar/italic.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/link.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
21 |
22 |
23 |
45 |
46 |
54 |
--------------------------------------------------------------------------------
/src/toolBar/list-ol.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/list-ul.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/picture.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
28 |
29 |
30 |
157 |
158 |
163 |
--------------------------------------------------------------------------------
/src/toolBar/quote-left.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/repeat.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/separator.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | |
4 |
5 |
6 |
7 |
12 |
13 |
18 |
--------------------------------------------------------------------------------
/src/toolBar/strikethrough.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/subscript.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/superscript.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/table.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
17 |
81 |
82 |
99 |
--------------------------------------------------------------------------------
/src/toolBar/thumb-tack.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/toolBar/trash.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
27 |
28 |
33 |
--------------------------------------------------------------------------------
/src/toolBar/underline.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
23 |
24 |
29 |
--------------------------------------------------------------------------------
/src/toolBar/undo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/webpack.build.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var webpack = require('webpack')
3 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
4 |
5 | function resolve (dir) {
6 | return path.join(__dirname, '..', dir)
7 | }
8 |
9 | const extractCSS = new ExtractTextPlugin('css/[name].css');
10 |
11 | module.exports = {
12 | entry: path.resolve(__dirname, './src/index.js'),
13 | output: {
14 | path: path.resolve(__dirname, './dist'),
15 | publicPath: './',
16 | filename: 'vue-bl-mark-down-editor.js',
17 | chunkFilename: 'js/[name].js',
18 | library: 'MarkDownEditor',
19 | libraryTarget: 'umd',
20 | umdNamedDefine: true
21 | },
22 | module: {
23 | rules: [
24 | {
25 | test: /\.css$/,
26 | use: extractCSS.extract({
27 | fallback: "style-loader",
28 | use: [
29 | "css-loader",
30 | "vue-style-loader"
31 | ]
32 | })
33 | // use: [
34 | // 'vue-style-loader',
35 | // 'css-loader'
36 | // ],
37 | },
38 | {
39 | test: /\.vue$/,
40 | loader: 'vue-loader',
41 | options: {
42 | extractCSS: true,
43 | loaders: {
44 | }
45 | // other vue-loader options go here
46 | }
47 | },
48 | {
49 | test: /\.js$/,
50 | loader: 'babel-loader',
51 | exclude: /node_modules/
52 | },
53 | {
54 | test: /\.(png|jpg|gif)$/,
55 | loader: 'file-loader',
56 | options: {
57 | limit: 10000,
58 | publicPath: '../',
59 | name: 'images/[name].[ext]?[hash:7]'
60 | }
61 | },
62 | //{ test: /\.(woff|ttf|eot|svg)$/, loader: 'url-loader?name=font/[name].[hash:7].[ext]&publicPath=../' },
63 | {
64 | test: /\.(eot|ttf|woff|woff2|svg)(\?.*)?$/,
65 | loader: 'file-loader',
66 | options: {
67 | limit: 10000,
68 | publicPath: '../',
69 | name: 'fonts/[name].[hash:7].[ext]'
70 | }
71 | }
72 | ]
73 | },
74 | resolve: {
75 | alias: {
76 | 'vue$': 'vue/dist/vue.esm.js',
77 | '@': resolve('src'),
78 | },
79 | extensions: ['*', '.js', '.vue', '.json']
80 | },
81 | devServer: {
82 | historyApiFallback: true,
83 | noInfo: true,
84 | overlay: true
85 | },
86 | performance: {
87 | hints: false
88 | },
89 | devtool: '#source-map',
90 | plugins:[
91 | // 分离css
92 | extractCSS,
93 | new webpack.DefinePlugin({
94 | 'process.env': {
95 | NODE_ENV: '"production"'
96 | }
97 | }),
98 | new webpack.optimize.UglifyJsPlugin({
99 | sourceMap: false,
100 | compress: {
101 | warnings: false,
102 | drop_debugger: true,
103 | drop_console: true
104 | }
105 | }),
106 | new webpack.LoaderOptionsPlugin({
107 | minimize: true
108 | }),
109 | ]
110 | }
111 |
112 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var webpack = require('webpack')
3 | var HtmlWebpackPlugin = require('html-webpack-plugin')
4 |
5 | function resolve (dir) {
6 | return path.join(__dirname, '..', dir)
7 | }
8 |
9 | module.exports = {
10 | entry: './src/dev/main.js',
11 | output: {
12 | path: path.resolve(__dirname, './html'),
13 | publicPath: '/',
14 | filename: '[name].[hash:7].js',
15 | },
16 | module: {
17 | rules: [
18 | {
19 | test: /\.css$/,
20 | use: [
21 | 'vue-style-loader',
22 | 'css-loader'
23 | ],
24 | }, {
25 | test: /\.vue$/,
26 | loader: 'vue-loader',
27 | options: {
28 | loaders: {
29 | }
30 | // other vue-loader options go here
31 | }
32 | },
33 | {
34 | test: /\.js$/,
35 | loader: 'babel-loader',
36 | exclude: /node_modules/
37 | },
38 | {
39 | test: /\.(png|jpg|gif|svg)$/,
40 | loader: 'file-loader',
41 | options: {
42 | name: 'images/[name].[ext]?[hash]'
43 | }
44 | },
45 | {
46 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
47 | loader: 'url-loader',
48 | options: {
49 | limit: 10000,
50 | name: 'fonts/[name].[hash:7].[ext]'
51 | }
52 | }
53 | ]
54 | },
55 | resolve: {
56 | alias: {
57 | 'vue$': 'vue/dist/vue.esm.js',
58 | '@': resolve('src'),
59 | },
60 | extensions: ['*', '.js', '.vue', '.json']
61 | },
62 | devServer: {
63 | historyApiFallback: true,
64 | noInfo: true,
65 | overlay: true
66 | },
67 | performance: {
68 | hints: false
69 | },
70 | devtool: '#eval-source-map',
71 | plugins: [
72 | new HtmlWebpackPlugin({
73 | filename: path.resolve(__dirname, './html/index.html'),
74 | template: path.resolve(__dirname, './src/dev/index.html'),
75 | inject: true
76 | }),
77 | ]
78 | }
79 |
80 | if (process.env.NODE_ENV === 'production') {
81 | module.exports.output = {
82 | path: path.resolve(__dirname, './html'),
83 | publicPath: './',
84 | filename: '[name].[hash:7].js',
85 | },
86 | module.exports.devtool = '#source-map'
87 | // http://vue-loader.vuejs.org/en/workflow/production.html
88 | module.exports.plugins = (module.exports.plugins || []).concat([
89 | new webpack.DefinePlugin({
90 | 'process.env': {
91 | NODE_ENV: '"production"'
92 | }
93 | }),
94 | new webpack.optimize.UglifyJsPlugin({
95 | sourceMap: false,
96 | compress: {
97 | warnings: false
98 | }
99 | }),
100 | new webpack.LoaderOptionsPlugin({
101 | minimize: true
102 | }),
103 |
104 | ])
105 | }
106 |
--------------------------------------------------------------------------------