为vue-maarkdown添加固定目录
This commit is contained in:
commit
debadf7236
34
App.vue
34
App.vue
@ -4,9 +4,9 @@
|
||||
v-model="val"
|
||||
@on-ready="onReady"
|
||||
@on-copy="onCopy"
|
||||
@on-paste-image="onPasteImage"
|
||||
@on-upload-image="onUpladImage"
|
||||
@on-save="onSave"
|
||||
:height="500"
|
||||
:height="fullHeight"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -21,11 +21,33 @@
|
||||
components: {
|
||||
Markdown
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
val: ''
|
||||
mounted() {
|
||||
const that = this
|
||||
window.onresize = () => {
|
||||
return (() => {
|
||||
window.fullHeight = document.documentElement.clientHeight
|
||||
that.fullHeight = window.fullHeight
|
||||
})()
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
val: '',
|
||||
fullHeight: document.documentElement.clientHeight
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
fullHeight(val) {
|
||||
if (!this.timer) {
|
||||
this.fullHeight = val
|
||||
this.timer = true
|
||||
let that = this
|
||||
setTimeout(function() {
|
||||
that.timer = false
|
||||
}, 400)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onReady(data) {
|
||||
console.log(data)
|
||||
@ -33,7 +55,7 @@
|
||||
onCopy(text) {
|
||||
console.log(text);
|
||||
},
|
||||
onPasteImage(file) {
|
||||
onUpladImage(file) {
|
||||
console.log(file)
|
||||
},
|
||||
onSave(data) {
|
||||
|
10
README.md
10
README.md
@ -342,7 +342,15 @@ export default index.setOptions({
|
||||
|
||||
预览区域和文档预览组件暂不支持自动生成目录,实现自动生成目录思路目前想到的大致有
|
||||
- 重写`renderer.heading` 方法,为生成的标题添加id,输入特定快捷键,如`[TOC]`时,查找预览区域内的的所有标题标签,分析等级关系,生成目录标签
|
||||
|
||||
属性 | 说明| 类型| 默认值
|
||||
-|-|-|-
|
||||
affix |固定模式| Boolean |true
|
||||
offset-top |距离窗口顶部达到指定偏移量后触发 |Number| 0
|
||||
offset-bottom |距离窗口底部达到指定偏移量后触发| Number| -
|
||||
bounds| 锚点区域边界,单位:px| Number| 5
|
||||
scroll-offset| 点击滚动的额外距离| Number| 0
|
||||
container| 指定滚动的容器 |String | HTMLElement| -
|
||||
show-ink| 是否显示小圆点| Boolean |false
|
||||
### icon替换
|
||||
项目内所有的icon和命名参考`/assets/font/index.html`,替换时需注意,预览区域的checkbox为icon,注意一并替换,
|
||||
修改`/assets/css/index.less`内的`input[type="checkbox"]`的`:after`样式。
|
||||
|
@ -1,3 +1,8 @@
|
||||
### 2.1.0
|
||||
- onSave 事件内返回结果增加html字段
|
||||
- 头部按钮增加本地图片上传,默认关闭
|
||||
- 增加on-upload-image事件,废除on-paste-image事件
|
||||
|
||||
### 2.0.0
|
||||
- 新增专业版编辑器
|
||||
- 新增预览组件
|
||||
|
@ -5,9 +5,13 @@
|
||||
编辑器保存事件,自动保存或者手动保存时触发,支持`ctrl+s`或`command+s`触发保存,返回值类型为`Object`,包含当前输入值`value`和选择的代码块主题`theme`。
|
||||
|
||||
|
||||
### on-paste-image
|
||||
### on-paste-image [已废除,可改用on-upload-image]
|
||||
|
||||
监听编辑器粘贴图片事件,在编辑区域内手动粘贴图片时触发,可用于支持粘贴插入图片文件,返回`file`文件,上传文件后可结合`on-ready`事件内返回的`insertContent`插入图片。
|
||||
|
||||
### on-copy
|
||||
复制代码块内容,触发时返回当前代码块的text,copyCode开启时才有效。
|
||||
|
||||
### on-upload-image
|
||||
|
||||
图片上传事件,用户自定义上传图片,复制粘贴图片截图、文件和点开菜单栏上传按钮时式触发,返回`file`文件,上传文件后可结合`on-ready`事件内返回的`insertContent`插入图片。
|
||||
|
@ -47,6 +47,7 @@ ol|有序列表|是
|
||||
code |代码块|是
|
||||
link |链接|是
|
||||
image|image|是
|
||||
uploadImage|本地图片|否
|
||||
table |表格|是
|
||||
checked|已完成列表|是
|
||||
notChecked |未完成列表|是
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vue-meditor",
|
||||
"description": "一款使用marked和highlight.js开发的一款markdown编辑器",
|
||||
"version": "2.0.3",
|
||||
"version": "2.1.0",
|
||||
"author": "zhaoxuhui<1258835133@qq.com>",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
|
81
src/assets/css/anchor.less
Normal file
81
src/assets/css/anchor.less
Normal file
@ -0,0 +1,81 @@
|
||||
// Anchor
|
||||
@primary-color : #2d8cf0;
|
||||
@anchor-border-width: 2px;
|
||||
@border-color-split : #e8eaec; // inside
|
||||
@body-background : #fff;
|
||||
@transition-time : .2s;
|
||||
@text-color : #515a6e;
|
||||
.anchor{
|
||||
&-wrapper{
|
||||
overflow: auto;
|
||||
padding-left: 4px;
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
||||
&{
|
||||
position: relative;
|
||||
padding-left: @anchor-border-width;
|
||||
|
||||
&-ink {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
&:before {
|
||||
content: ' ';
|
||||
position: relative;
|
||||
width: @anchor-border-width;
|
||||
height: 100%;
|
||||
display: block;
|
||||
background-color: @border-color-split;
|
||||
margin: 0 auto;
|
||||
}
|
||||
&-ball {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid @primary-color;
|
||||
background-color: @body-background;
|
||||
left: 50%;
|
||||
transition: top @transition-time ease-in-out;
|
||||
transform: translate(-50%, 2px);
|
||||
}
|
||||
}
|
||||
|
||||
&.fixed &-ink &-ink-ball {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-link {
|
||||
padding: 8px 0 6px 16px !important;
|
||||
line-height: 1;
|
||||
|
||||
&-title {
|
||||
font-size: 12px;
|
||||
text-decoration:none;
|
||||
display: block;
|
||||
position: relative;
|
||||
transition: all .3s;
|
||||
color: @text-color;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom: 8px;
|
||||
&:only-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-active > &-title {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-link &-link {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
@import './anchor.less';
|
||||
@margin: 8px 0;
|
||||
@line-height: 22px;
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -20,9 +20,6 @@ Created by iconfont
|
||||
/>
|
||||
<missing-glyph />
|
||||
|
||||
<glyph glyph-name="fujian" unicode="" d="M874.7 722.4q32.4-32.4 48.15-72.45t15.75-82.35-15.75-82.35-48.15-72.45l-344.7-342q-32.4-32.4-79.65-54t-99.9-25.2-107.55 14.4-101.7 64.8q-45.9 45.9-63.9 99.45t-14.4 106.65 24.75 99.9 53.55 79.2l306 303.3q8.1 8.1 23.85 4.05t23.85-12.15q7.2-8.1 11.7-23.85t-3.6-23.85l-305.1-302.4q-24.3-24.3-40.5-56.7t-18.9-69.3 10.8-74.7 48.6-72.9q31.5-31.5 69.75-44.55t76.05-10.8 72 16.65 58.5 38.7l343.8 341.1q24.3 24.3 34.65 50.4t9.9 51.75-12.15 49.95-32.4 45q-38.7 37.8-86.85 35.55t-96.75-50.85l-310.5-308.7q-20.7-20.7-20.25-45.9t16.65-41.4q19.8-19.8 45.45-16.2t41.85 19.8l281.7 279.9q8.1 8.1 23.85 4.05t23.85-12.15 12.6-23.85-3.6-23.85l-282.6-279.9q-32.4-32.4-62.1-45t-55.8-11.25-48.6 13.95-41.4 30.6q-15.3 14.4-27.45 36.9t-13.95 49.5 9.45 57.6 43.65 62.1q16.2 16.2 28.8 29.7 10.8 11.7 20.25 20.7t10.35 9.9l251.1 250.2q32.4 32.4 70.65 50.85t77.85 21.15 77.85-10.8 70.65-45.9z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="md" unicode="" d="M471.289899 141.73866699999996c-6.827051 16.384-16.043051 37.205333-27.648 62.464-10.923008 25.258667-22.869683 52.565333-35.839991 81.92-12.970995 29.354667-26.965649 59.392-41.984 90.112-14.336286 31.402667-27.989606 60.757333-40.96 88.064-12.970914 27.989333-25.258901 52.906667-36.864 74.752-10.92288 21.841067-19.797538 38.5664-26.624 50.176-7.509521-80.554667-13.653517-167.936-18.432-262.144-4.778842-93.525333-8.874837-188.074667-12.288-283.648l-97.28 0c2.730581 61.44 5.80259 123.221333 9.216 185.344 3.413248 62.805333 7.167915 124.245333 11.264 184.32 4.778573 60.757333 9.557235 119.808 14.336 177.152 5.461222 57.339733 11.263885 111.611733 17.408 162.816l87.04 0c18.431774-30.0416 38.229086-65.540267 59.392-106.496 21.162377-40.964267 42.325022-83.972267 63.488-129.024 21.162313-44.373333 41.642274-89.088 61.439991-134.144 19.796949-44.373333 37.887573-84.992 54.272-121.856 16.383531 36.864 34.474197 77.482667 54.272 121.856 19.796821 45.056 40.276821 89.770667 61.44 134.144 21.162112 45.051733 42.324736 88.059733 63.488 129.024 21.162027 40.955733 40.95936 76.4544 59.392 106.496l87.04 0c22.126421-218.018133 38.979968-435.549867 50.560683-667.84 0.56704-11.370667 44.931072 2.005333 45.472853-9.412267l-82.925739-85.713067-109.592363 85.713067c-0.523093 14.647467 50.366165-3.114667 49.810987 11.485867-3.06816 80.661333-6.62464 160.5888-10.670421 239.7824-4.779349 94.208-10.923349 181.589333-18.432 262.144-6.827349-11.6096-16.043349-28.334933-27.648-50.176-10.923307-21.845333-22.869973-46.762667-35.84-74.752-12.971264-27.306667-26.965931-56.661333-41.984-88.064-14.336555-30.72-27.989888-60.757333-40.96-90.112-12.971179-29.354667-25.259179-56.661333-36.864-81.92-10.923179-25.258667-19.797803-46.08-26.624-62.464l-79.872 0" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
@ -62,9 +59,6 @@ Created by iconfont
|
||||
<glyph glyph-name="under-line" unicode="" d="M166.12017822 73.63925171000005h684.40402222v-57.03552246H166.12017822v57.03552246z m0 684.40237427h294.74972535v-44.84481812l-63.66192628-4.48406982-14.53656005-13.00396729v-321.53302002c0-61.28723145 13.0328064-105.0856018 39.09924316-131.39511108 26.06643677-26.3053894 68.83895874-39.46426391 128.32580567-39.46426392 54.80667114 0 94.48928833 13.97872925 119.05197143 41.93206787 24.5626831 27.95745849 36.84402466 73.7663269 36.84402466 137.4472046V692.12118531l-15.54016114 15.69424438-65.66665649 5.38055419V758.0416259799999h233.09335328v-44.84481812l-62.66326905-5.38055419-14.03640747-15.69424439v-318.84439087c0-85.20309448-21.38626099-146.93939209-64.16290283-185.20394898-42.77252198-38.26950073-111.78451538-57.40219117-207.02114869-57.40219115-49.79690552 0-93.48980713 6.50527954-131.08364868 19.50759887-37.5954895 13.00643921-67.0880127 31.61178589-88.47592163 55.83251953-16.37402344 19.13269043-27.98794555 41.03393555-34.83764649 65.69549561-6.85134888 24.66567993-10.27661133 60.01831055-10.27661132 106.05789184V695.70794677l-14.53656006 13.00396729-64.66305542 4.48406982V758.0416259799999z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="checked1" unicode="" d="M810.666667 768H213.333333c-47.146667 0-85.333333-38.186667-85.333333-85.333333v-597.333334c0-47.146667 38.186667-85.333333 85.333333-85.333333h597.333334c47.146667 0 85.333333 38.186667 85.333333 85.333333V682.666667c0 47.146667-38.186667 85.333333-85.333333 85.333333zM426.666667 170.66666699999996L213.333333 384l60.373334 60.373333L426.666667 291.41333299999997l323.626666 323.626667L810.666667 554.666667 426.666667 170.66666699999996z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="close" unicode="" d="M869.16373333 77.84106667000003l-303.82826666 303.82826666 303.82826666 303.95733334c14.62826667 14.62826667 14.62826667 38.448 0 53.07626666s-38.448 14.62826667-53.07626666 0l-303.82826667-303.82826666-303.95733333 303.82826666c-14.62826667 14.62826667-38.448 14.62826667-53.07626667 0s-14.62826667-38.448 0-53.07626666l303.95733333-303.95733334-303.95733333-303.82826666c-14.62826667-14.62826667-14.62826667-38.448 0-53.07626667s38.448-14.62826667 53.07626667 0l303.95733333 303.95733333 303.95733333-303.95733333c14.62826667-14.62826667 38.448-14.62826667 53.07626667 0 14.4992 14.62826667 14.4992 38.448-0.13013333 53.07626667z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
@ -110,9 +104,15 @@ Created by iconfont
|
||||
<glyph glyph-name="ol" unicode="" d="M349.29890773 763.63588267h542.33697494a54.23369707 54.23369707 0 0 0 0-108.4673952h-542.33697494a54.23369707 54.23369707 0 0 0 0 108.4673952z m0-325.4021856h542.33697494a54.23369707 54.23369707 0 0 0 0-108.46739414h-542.33697494a54.23369707 54.23369707 0 0 0 0 108.46739414z m0-325.40218454h542.33697494a54.23369707 54.23369707 0 0 0 0-108.4673952h-542.33697494a54.23369707 54.23369707 0 0 0 0 108.4673952zM97.1122144 760.3818602700001v25.48983786a167.03978773 167.03978773 0 0 1 24.9475008 0 35.25190293 35.25190293 0 0 1 18.43945707 10.30440214 33.08255573 33.08255573 0 0 1 7.59271786 13.55842453 31.45554453 31.45554453 0 0 1 0 7.59271787H182.801456v-189.27560427h-40.67527253V760.3818602700001z m0-420.3111552a108.4673952 108.4673952 0 0 0 37.96358827 40.6752736 279.8458784 279.8458784 0 0 1 33.6248928 26.57451093 39.59059947 39.59059947 0 0 1 11.93141333 27.65918613 30.3708704 30.3708704 0 0 1-6.50804373 20.06646827 23.86282667 23.86282667 0 0 1-19.5241312 8.13505387 23.3204896 23.3204896 0 0 1-26.03217494-12.4737504 63.45342613 63.45342613 0 0 1-4.33869546-23.3204896h-35.251904a92.7396224 92.7396224 0 0 0 8.677392 39.59059946 54.23369707 54.23369707 0 0 0 54.23369706 28.7438592 67.7921216 67.7921216 0 0 0 48.26799147-16.81244586 58.5723936 58.5723936 0 0 0 17.89712-44.471632 60.7417408 60.7417408 0 0 0-12.4737504-37.96358827 126.90685227 126.90685227 0 0 0-27.65918613-24.40516373l-15.18543467-10.8467392-19.5241312-14.64309867a42.8446208 42.8446208 0 0 1-8.677392-10.30440213h83.5198944v-33.08255574H86.80781227a90.02793813 90.02793813 0 0 0 8.67739093 37.4212512z m27.11684907-304.2510432a43.38695787 43.38695787 0 0 1 3.7963584-18.98179414 25.48983787 25.48983787 0 0 1 24.9475008-14.10076053 27.65918613 27.65918613 0 0 1 17.35478293 5.4233696 28.2015232 28.2015232 0 0 1 8.13505493 21.6934784 24.9475008 24.9475008 0 0 1-15.18543466 25.48983787 78.0965248 78.0965248 0 0 1-27.11684907 7.0503808v27.11684906a71.58848107 71.58848107 0 0 1 25.48983787 3.7963584 22.23581547 22.23581547 0 0 1 12.4737504 23.32049067 24.9475008 24.9475008 0 0 1-6.50804374 17.89712 23.3204896 23.3204896 0 0 1-17.89712 7.0503808 22.77815253 22.77815253 0 0 1-19.5241312-8.677392 35.79424 35.79424 0 0 1-5.96570666-22.77815253h-35.251904a94.90897067 94.90897067 0 0 0 4.8810336 27.116848 61.28407787 61.28407787 0 0 0 14.6430976 20.60880533 50.97967573 50.97967573 0 0 0 17.354784 10.30440213 76.4695136 76.4695136 0 0 0 24.9475008 2.71168534 66.70744747 66.70744747 0 0 0 44.47163093-14.1007616 47.18331627 47.18331627 0 0 0 16.81244693-37.96358827 42.30228373 42.30228373 0 0 0-10.3044032-28.7438592 34.16722987 34.16722987 0 0 0-13.01608746-9.76206613 27.11684907 27.11684907 0 0 0 14.64309866-8.67739094 47.18331627 47.18331627 0 0 0 14.64309867-36.33657706 61.28407787 61.28407787 0 0 0-16.81244587-42.84462187 64.53810027 64.53810027 0 0 0-49.89500266-18.43945707 60.7417408 60.7417408 0 0 0-54.23369707 26.574512 79.18119787 79.18119787 0 0 0-9.219728 36.87891414z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="upload-img" unicode="" d="M885.94295467 791.41546667h-748.96725334C102.64029867 791.41546667 74.5472 762.93461333 74.5472 728.113152V95.09546666999995c0-34.816 28.09309867-63.30231467 62.42304-63.30231467h530.51938133V95.09546666999995h-499.318784c-18.726912 0-31.20059733 15.826944-31.20059733 31.653888V696.46472533c0 15.82148267 12.47368533 31.64842667 31.20059733 31.64842667h686.56605867c18.73237333 0 31.20605867-15.826944 31.20605867-31.64842667v-411.46231466H948.36053333v443.11074133c0 34.82146133-28.082176 63.30231467-62.41757866 63.30231467zM819.02523733 296.52582399999994c-6.23684267 6.30237867-12.46276267 9.44810667-21.81256533 9.44810667-9.34434133 0-15.58664533-3.145728-21.81256533-9.44810667l-87.25572267-88.15684267c-12.468224-12.599296-12.468224-31.490048 0-44.089344 12.46276267-12.58837333 31.162368-12.58837333 43.62513067 0l34.28625066 34.64123734v-195.22082134C766.05576533-12.039509329999987 778.51306667-27.784533330000045 797.212672-27.784533330000045c18.694144 0 31.15690667 12.59383467 31.15690667 31.48458666v195.22082134l37.39921066-37.781504c12.46276267-12.59383467 31.162368-12.59383467 43.630592 0 12.46276267 12.58837333 12.46276267 31.490048 0 44.07842133l-90.374144 91.308032z m-408.223744 236.158976c15.57572267 34.63031467 6.23684267 75.56846933-18.69960533 103.90186667-28.04394667 28.33885867-71.67453867 34.63031467-105.94440533 22.04194133-34.291712-15.75594667-56.098816-53.53745067-56.098816-91.31895467 0-50.37533867 40.50670933-91.308032 90.36868266-91.308032 37.404672 0 74.79842133 22.03648 90.374144 56.672256v0.01092267zM292.388864 567.31511467c0 9.44810667 3.10749867 15.745024 9.34980267 22.04194133 3.10749867 9.44264533 12.45730133 12.59383467 21.80164266 12.59383467 15.58664533 0 31.17329067-12.59383467 31.17329067-28.33885867 0-15.73956267-12.47368533-31.48458667-28.049408-34.63031467-18.70506667 0-34.28078933 12.58837333-34.28078933 28.33339734h0.00546133z m-104.333312-384.62532267c12.54468267-12.53376 31.36989867-12.53376 43.91458133 0l131.72736 131.57444267 109.7728-109.64718934c3.14026667-3.129344 3.14026667-3.129344 6.275072-3.129344 12.550144-6.26961067 25.08936533-6.26961067 37.63950934 3.129344L827.88352 514.74432c12.54468267 12.53376 12.54468267 31.326208 0 43.86542933-12.55560533 12.52829867-31.37536 12.52829867-43.91458133 0l-288.54408534-288.21640533L385.65751467 383.17533867l-3.13480534 3.13480533c-12.550144 9.39349333-28.229632 9.39349333-40.779776-3.13480533L184.93166933 226.53883732999998c-9.404416-12.53376-9.404416-31.33166933 3.13480534-43.859968l-0.01092267 0.01092267z m0 0" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="save" unicode="" d="M788.69067383 752.15185547h-26.03759766v0.65917969H261.42932128v-0.65917969h-26.03759764c-50.92163086 0-92.20275879-41.19873047-92.2027588-92.12036133v-552.72216797c0-50.92163086 41.28112793-92.12036133 92.2027588-92.12036133h553.29895019c50.92163086 0 92.20275879 41.28112793 92.20275879 92.12036133V660.03149414c0 50.8392334-41.36352539 92.12036133-92.20275879 92.12036133z m-467.93518067-58.58459473H703.16210938v-118.48754882c0-21.83532715-19.03381348-39.55078125-42.51708985-39.55078125H363.272583c-23.48327637 0-42.51708984 17.71545411-42.51708983 39.55078125V693.5672607500001z m501.47094727-575.46386719c0-24.38964844-19.85778809-44.24743653-44.32983399-44.24743652H246.02099609c-24.4720459 0-44.32983398 19.77539063-44.32983398 44.24743652V649.31982422c0 24.38964844 19.85778809 44.24743653 44.32983398 44.24743653h15.32592774V568.5703125c0-50.92163086 40.78674317-92.12036133 91.1315918-92.12036133h318.96057129c50.34484864 0 91.1315918 41.28112793 91.13159179 92.12036133V693.5672607500001h15.24353027c24.4720459 0 44.32983398-19.77539063 44.32983399-44.24743653v-531.21643067zM607.58105469 568.2407226600001c16.31469727 0 29.66308594 13.34838867 29.66308593 29.66308593v32.95898438c0 16.31469727-13.34838867 29.66308594-29.66308593 29.66308594s-29.66308594-13.34838867-29.66308594-29.66308594v-32.95898438c0-16.31469727 13.34838867-29.66308594 29.66308594-29.66308593z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="check-box" unicode="" d="M810.666667 768H213.333333c-47.146667 0-85.333333-38.186667-85.333333-85.333333v-597.333334c0-47.146667 38.186667-85.333333 85.333333-85.333333h597.333334c47.146667 0 85.333333 38.186667 85.333333 85.333333V682.666667c0 47.146667-38.186667 85.333333-85.333333 85.333333zM426.666667 170.66666699999996L213.333333 384l60.373334 60.373333L426.666667 291.41333299999997l323.626666 323.626667L810.666667 554.666667 426.666667 170.66666699999996z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
|
||||
|
||||
</font>
|
||||
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 42 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
19
src/assets/js/marked/createToc.js
Normal file
19
src/assets/js/marked/createToc.js
Normal file
@ -0,0 +1,19 @@
|
||||
export default {
|
||||
add(text, level) {
|
||||
const anchor = `toc${level}${++this.index}`;
|
||||
const item = { anchor, level, text };
|
||||
const items = this.tocItems;
|
||||
|
||||
if (item.level <= 3) {
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
return anchor;
|
||||
},
|
||||
reset: function() {
|
||||
this.tocItems = [];
|
||||
this.index = 0;
|
||||
},
|
||||
tocItems: [],
|
||||
index: 0
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
import tocObj from './createToc';
|
||||
var block = {
|
||||
newline: /^\n+/,
|
||||
code: /^( {4}[^\n]+\n*)+/,
|
||||
@ -947,20 +948,22 @@ Renderer.prototype.html = function (html) {
|
||||
};
|
||||
|
||||
Renderer.prototype.heading = function (text, level, raw, slugger) {
|
||||
if (this.options.headerIds) {
|
||||
return '<h'
|
||||
+ level
|
||||
+ ' id="'
|
||||
+ this.options.headerPrefix
|
||||
+ slugger.slug(raw)
|
||||
+ '">'
|
||||
+ text
|
||||
+ '</h'
|
||||
+ level
|
||||
+ '>\n';
|
||||
}
|
||||
// ignore IDs
|
||||
return '<h' + level + '>' + text + '</h' + level + '>\n';
|
||||
// if (this.options.headerIds) {
|
||||
// return '<h'
|
||||
// + level
|
||||
// + ' id="'
|
||||
// + this.options.headerPrefix
|
||||
// + slugger.slug(raw)
|
||||
// + '">'
|
||||
// + text
|
||||
// + '</h'
|
||||
// + level
|
||||
// + '>\n';
|
||||
// }
|
||||
// // ignore IDs
|
||||
// return '<h' + level + '>' + text + '</h' + level + '>\n';
|
||||
let anchor = tocObj.add(text, level);
|
||||
return `<a id="${anchor}" href="#${anchor}" class="anchor-fix"><h${level}>${text}</h${level}></a>\n`;
|
||||
};
|
||||
|
||||
Renderer.prototype.hr = function () {
|
||||
|
155
src/components/Anchor/affix.vue
Normal file
155
src/components/Anchor/affix.vue
Normal file
@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div>
|
||||
<div ref="point" :class="classes" :style="styles">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div v-show="slot" :style="slotStyle"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { on, off } from '../Anchor/util';
|
||||
const prefixCls = 'affix';
|
||||
|
||||
function getScroll(target, top) {
|
||||
const prop = top ? 'pageYOffset' : 'pageXOffset';
|
||||
const method = top ? 'scrollTop' : 'scrollLeft';
|
||||
|
||||
let ret = target[prop];
|
||||
|
||||
if (typeof ret !== 'number') {
|
||||
ret = window.document.documentElement[method];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getOffset(element) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
const scrollTop = getScroll(window, true);
|
||||
const scrollLeft = getScroll(window);
|
||||
|
||||
const docEl = window.document.body;
|
||||
const clientTop = docEl.clientTop || 0;
|
||||
const clientLeft = docEl.clientLeft || 0;
|
||||
|
||||
return {
|
||||
top: rect.top + scrollTop - clientTop,
|
||||
left: rect.left + scrollLeft - clientLeft
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'Affix',
|
||||
props: {
|
||||
offsetTop: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
offsetBottom: {
|
||||
type: Number
|
||||
},
|
||||
useCapture: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
affix: false,
|
||||
styles: {},
|
||||
slot: false,
|
||||
slotStyle: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
offsetType () {
|
||||
let type = 'top';
|
||||
if (this.offsetBottom >= 0) {
|
||||
type = 'bottom';
|
||||
}
|
||||
|
||||
return type;
|
||||
},
|
||||
classes () {
|
||||
return [
|
||||
{
|
||||
[`${prefixCls}`]: this.affix
|
||||
}
|
||||
];
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// window.addEventListener('scroll', this.handleScroll, false);
|
||||
// window.addEventListener('resize', this.handleScroll, false);
|
||||
on(window, 'scroll', this.handleScroll, this.useCapture);
|
||||
on(window, 'resize', this.handleScroll, this.useCapture);
|
||||
this.$nextTick(() => {
|
||||
this.handleScroll();
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
// window.removeEventListener('scroll', this.handleScroll, false);
|
||||
// window.removeEventListener('resize', this.handleScroll, false);
|
||||
off(window, 'scroll', this.handleScroll, this.useCapture);
|
||||
off(window, 'resize', this.handleScroll, this.useCapture);
|
||||
},
|
||||
methods: {
|
||||
handleScroll () {
|
||||
const affix = this.affix;
|
||||
const scrollTop = getScroll(window, true);
|
||||
const elOffset = getOffset(this.$el);
|
||||
const windowHeight = window.innerHeight;
|
||||
const elHeight = this.$el.getElementsByTagName('div')[0].offsetHeight;
|
||||
|
||||
// Fixed Top
|
||||
if ((elOffset.top - this.offsetTop) < scrollTop && this.offsetType == 'top' && !affix) {
|
||||
this.affix = true;
|
||||
this.slotStyle = {
|
||||
width: this.$refs.point.clientWidth + 'px',
|
||||
height: this.$refs.point.clientHeight + 'px'
|
||||
};
|
||||
this.slot = true;
|
||||
this.styles = {
|
||||
top: `${this.offsetTop}px`,
|
||||
left: `${elOffset.left}px`,
|
||||
width: `${this.$el.offsetWidth}px`
|
||||
};
|
||||
|
||||
this.$emit('on-change', true);
|
||||
} else if ((elOffset.top - this.offsetTop) > scrollTop && this.offsetType == 'top' && affix) {
|
||||
this.slot = false;
|
||||
this.slotStyle = {};
|
||||
this.affix = false;
|
||||
this.styles = null;
|
||||
|
||||
this.$emit('on-change', false);
|
||||
}
|
||||
|
||||
// Fixed Bottom
|
||||
if ((elOffset.top + this.offsetBottom + elHeight) > (scrollTop + windowHeight) && this.offsetType == 'bottom' && !affix) {
|
||||
this.affix = true;
|
||||
this.styles = {
|
||||
bottom: `${this.offsetBottom}px`,
|
||||
left: `${elOffset.left}px`,
|
||||
width: `${this.$el.offsetWidth}px`
|
||||
};
|
||||
|
||||
this.$emit('on-change', true);
|
||||
} else if ((elOffset.top + this.offsetBottom + elHeight) < (scrollTop + windowHeight) && this.offsetType == 'bottom' && affix) {
|
||||
this.affix = false;
|
||||
this.styles = null;
|
||||
|
||||
this.$emit('on-change', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="less">
|
||||
.affix {
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
</style>
|
81
src/components/Anchor/anchor.less
Normal file
81
src/components/Anchor/anchor.less
Normal file
@ -0,0 +1,81 @@
|
||||
// Anchor
|
||||
@primary-color : #2d8cf0;
|
||||
@anchor-border-width: 2px;
|
||||
@border-color-split : #e8eaec; // inside
|
||||
@body-background : #fff;
|
||||
@transition-time : .2s;
|
||||
@text-color : #515a6e;
|
||||
.anchor{
|
||||
&-wrapper{
|
||||
overflow: auto;
|
||||
padding-left: 4px;
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
||||
&{
|
||||
position: relative;
|
||||
padding-left: @anchor-border-width;
|
||||
|
||||
&-ink {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
&:before {
|
||||
content: ' ';
|
||||
position: relative;
|
||||
width: @anchor-border-width;
|
||||
height: 100%;
|
||||
display: block;
|
||||
background-color: @border-color-split;
|
||||
margin: 0 auto;
|
||||
}
|
||||
&-ball {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid @primary-color;
|
||||
background-color: @body-background;
|
||||
left: 50%;
|
||||
transition: top @transition-time ease-in-out;
|
||||
transform: translate(-50%, 2px);
|
||||
}
|
||||
}
|
||||
|
||||
&.fixed &-ink &-ink-ball {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-link {
|
||||
padding: 8px 0 6px 16px;
|
||||
line-height: 1;
|
||||
|
||||
&-title {
|
||||
font-size: 12px;
|
||||
text-decoration:none;
|
||||
display: block;
|
||||
position: relative;
|
||||
transition: all .3s;
|
||||
color: @text-color;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom: 8px;
|
||||
&:only-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-active > &-title {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-link &-link {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
}
|
207
src/components/Anchor/anchor.vue
Normal file
207
src/components/Anchor/anchor.vue
Normal file
@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<component :is="wrapperComponent" :offset-top="offsetTop" :offset-bottom="offsetBottom" @on-change="handleAffixStateChange">
|
||||
<div :class="`${prefix}-wrapper`" :style="wrapperStyle">
|
||||
<div :class="`${prefix}`">
|
||||
<div :class="`${prefix}-ink`">
|
||||
<span v-show="showInk" :class="`${prefix}-ink-ball`" :style="{top: `${inkTop}px`}"></span>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
<script>
|
||||
import { scrollTop, findComponentsDownward, sharpMatcherRegx } from './util';
|
||||
import Affix from './affix';
|
||||
import { on, off } from './util';
|
||||
export default {
|
||||
name: 'Anchor',
|
||||
components:{Affix},
|
||||
provide () {
|
||||
return {
|
||||
anchorCom: this
|
||||
};
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefix: 'anchor',
|
||||
isAffixed: false, // current affixed state
|
||||
inkTop: 0,
|
||||
animating: false, // if is scrolling now
|
||||
currentLink: '', // current show link => #href -> currentLink = #href
|
||||
currentId: '', // current show title id => #href -> currentId = href
|
||||
scrollContainer: null,
|
||||
scrollElement: null,
|
||||
titlesOffsetArr: [],
|
||||
wrapperTop: 0,
|
||||
upperFirstTitle: true
|
||||
};
|
||||
},
|
||||
props: {
|
||||
affix: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
offsetTop: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
offsetBottom: Number,
|
||||
bounds: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
// container: [String, HTMLElement], // HTMLElement 在 SSR 下不支持
|
||||
container: null,
|
||||
showInk: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
scrollOffset: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wrapperComponent () {
|
||||
return this.affix ? 'Affix' : 'div';
|
||||
},
|
||||
wrapperStyle () {
|
||||
return {
|
||||
maxHeight: this.offsetTop ? `calc(100vh - ${this.offsetTop}px)` : '100vh'
|
||||
};
|
||||
},
|
||||
containerIsWindow () {
|
||||
return this.scrollContainer === window;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleAffixStateChange (state) {
|
||||
this.isAffixed = this.affix && state;
|
||||
},
|
||||
handleScroll (e) {
|
||||
console.log(this.titlesOffsetArr,'this.titlesOffsetArr[0]');
|
||||
this.upperFirstTitle = e.target.scrollTop < this.titlesOffsetArr[0].offset;
|
||||
if (this.animating) return;
|
||||
this.updateTitleOffset();
|
||||
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop || e.target.scrollTop;
|
||||
this.getCurrentScrollAtTitleId(scrollTop);
|
||||
},
|
||||
handleHashChange () {
|
||||
const url = window.location.href;
|
||||
const sharpLinkMatch = sharpMatcherRegx.exec(url);
|
||||
if (!sharpLinkMatch) return;
|
||||
this.currentLink = sharpLinkMatch[0];
|
||||
this.currentId = sharpLinkMatch[1];
|
||||
},
|
||||
handleScrollTo () {
|
||||
const anchor = document.getElementById(this.currentId);
|
||||
const currentLinkElementA = document.querySelector(`a[data-href="${this.currentLink}"]`);
|
||||
let offset = this.scrollOffset;
|
||||
if (currentLinkElementA) {
|
||||
offset = parseFloat(currentLinkElementA.getAttribute('data-scroll-offset'));
|
||||
}
|
||||
|
||||
if (!anchor) return;
|
||||
const offsetTop = anchor.offsetTop - this.wrapperTop - offset;
|
||||
this.animating = true;
|
||||
scrollTop(this.scrollContainer, this.scrollElement.scrollTop, offsetTop, 600, () => {
|
||||
this.animating = false;
|
||||
});
|
||||
this.handleSetInkTop();
|
||||
},
|
||||
handleSetInkTop () {
|
||||
const currentLinkElementA = document.querySelector(`a[data-href="${this.currentLink}"]`);
|
||||
if (!currentLinkElementA) return;
|
||||
const elementATop = currentLinkElementA.offsetTop;
|
||||
const top = (elementATop < 0 ? this.offsetTop : elementATop);
|
||||
this.inkTop = top;
|
||||
},
|
||||
updateTitleOffset () {
|
||||
const links = findComponentsDownward(this, 'AnchorLink').map(link => {
|
||||
return link.href;
|
||||
});
|
||||
const idArr = links.map(link => {
|
||||
console.log(link,'link');
|
||||
return link.split('#')[1];
|
||||
});
|
||||
let offsetArr = [];
|
||||
idArr.forEach(id => {
|
||||
const titleEle = document.getElementById(id);
|
||||
if (titleEle) offsetArr.push({
|
||||
link: `#${id}`,
|
||||
offset: titleEle.offsetTop - this.scrollElement.offsetTop
|
||||
});
|
||||
});
|
||||
this.titlesOffsetArr = offsetArr;
|
||||
},
|
||||
getCurrentScrollAtTitleId (scrollTop) {
|
||||
let i = -1;
|
||||
let len = this.titlesOffsetArr.length;
|
||||
let titleItem = {
|
||||
link: '#',
|
||||
offset: 0
|
||||
};
|
||||
scrollTop += this.bounds;
|
||||
while (++i < len) {
|
||||
let currentEle = this.titlesOffsetArr[i];
|
||||
let nextEle = this.titlesOffsetArr[i + 1];
|
||||
if (scrollTop >= currentEle.offset && scrollTop < ((nextEle && nextEle.offset) || Infinity)) {
|
||||
titleItem = this.titlesOffsetArr[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.currentLink = titleItem.link;
|
||||
this.handleSetInkTop();
|
||||
},
|
||||
getContainer () {
|
||||
this.scrollContainer = this.container ? (typeof this.container === 'string' ? document.querySelector(this.container) : this.container) : window;
|
||||
this.scrollElement = this.container ? this.scrollContainer : (document.documentElement || document.body);
|
||||
},
|
||||
removeListener () {
|
||||
off(this.scrollContainer, 'scroll', this.handleScroll);
|
||||
off(window, 'hashchange', this.handleHashChange);
|
||||
},
|
||||
init () {
|
||||
// const anchorLink = findComponentDownward(this, 'AnchorLink');
|
||||
this.handleHashChange();
|
||||
this.$nextTick(() => {
|
||||
this.removeListener();
|
||||
this.getContainer();
|
||||
this.wrapperTop = this.containerIsWindow ? 0 : this.scrollElement.offsetTop;
|
||||
this.handleScrollTo();
|
||||
this.handleSetInkTop();
|
||||
this.updateTitleOffset();
|
||||
if (this.titlesOffsetArr[0]) {
|
||||
this.upperFirstTitle = this.scrollElement.scrollTop < this.titlesOffsetArr[0].offset;
|
||||
}
|
||||
on(this.scrollContainer, 'scroll', this.handleScroll);
|
||||
on(window, 'hashchange', this.handleHashChange);
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route' () {
|
||||
this.handleHashChange();
|
||||
this.$nextTick(() => {
|
||||
this.handleScrollTo();
|
||||
});
|
||||
},
|
||||
container () {
|
||||
this.init();
|
||||
},
|
||||
currentLink (newHref, oldHref) {
|
||||
this.$emit('on-change', newHref, oldHref);
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.init();
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.removeListener();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import "./anchor.less";
|
||||
</style>
|
62
src/components/Anchor/anchorLink.vue
Normal file
62
src/components/Anchor/anchorLink.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div :class="anchorLinkClasses">
|
||||
<a :class="linkTitleClasses" :href="href" :data-scroll-offset="scrollOffset" :data-href="href" @click.prevent="goAnchor" :title="title">{{ title }}</a>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'AnchorLink',
|
||||
inject: ['anchorCom'],
|
||||
props: {
|
||||
href: String,
|
||||
title: String,
|
||||
scrollOffset: {
|
||||
type: Number,
|
||||
default () {
|
||||
return this.anchorCom.scrollOffset;
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefix: 'anchor-link'
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
anchorLinkClasses () {
|
||||
return [
|
||||
this.prefix,
|
||||
this.anchorCom.currentLink === this.href ? `${this.prefix}-active` : ''
|
||||
];
|
||||
},
|
||||
linkTitleClasses () {
|
||||
return [
|
||||
`${this.prefix}-title`
|
||||
];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goAnchor () {
|
||||
this.currentLink = this.href;
|
||||
this.anchorCom.handleHashChange();
|
||||
this.anchorCom.handleScrollTo();
|
||||
this.anchorCom.$emit('on-select', this.href);
|
||||
const isRoute = this.$router;
|
||||
if (isRoute) {
|
||||
this.$router.push(this.href, () => {});
|
||||
} else {
|
||||
window.location.href = this.href;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.$nextTick(() => {
|
||||
this.anchorCom.init();
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import "./anchor.less";
|
||||
</style>
|
36
src/components/Anchor/anchorLinks.vue
Normal file
36
src/components/Anchor/anchorLinks.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 递归 render -->
|
||||
<template v-for="child in item">
|
||||
<AnchorLink :href="`#${child.anchor}`" :title="child.text" :key="child.anchor">
|
||||
<template v-if="child.children">
|
||||
<AnchorLinks :item="child.children"></AnchorLinks>
|
||||
</template>
|
||||
</AnchorLink>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
import AnchorLink from './anchorLink'
|
||||
|
||||
export default {
|
||||
mounted() {
|
||||
console.log(this.item, 'links')
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
},
|
||||
name: 'AnchorLinks',
|
||||
components: { AnchorLink }
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import './anchor.less';
|
||||
</style>
|
92
src/components/Anchor/util.js
Normal file
92
src/components/Anchor/util.js
Normal file
@ -0,0 +1,92 @@
|
||||
import Vue from "vue";
|
||||
const isServer = Vue.prototype.$isServer;
|
||||
// Find components downward
|
||||
|
||||
export function findComponentsDownward(context, componentName) {
|
||||
return context.$children.reduce((components, child) => {
|
||||
if (child.$options.name === componentName) components.push(child);
|
||||
const foundChilds = findComponentsDownward(child, componentName);
|
||||
return components.concat(foundChilds);
|
||||
}, []);
|
||||
}
|
||||
|
||||
// scrollTop animation
|
||||
export function scrollTop(el, from = 0, to, duration = 500, endCallback) {
|
||||
if (!window.requestAnimationFrame) {
|
||||
window.requestAnimationFrame =
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function(callback) {
|
||||
return window.setTimeout(callback, 1000 / 60);
|
||||
};
|
||||
}
|
||||
const difference = Math.abs(from - to);
|
||||
const step = Math.ceil((difference / duration) * 50);
|
||||
|
||||
function scroll(start, end, step) {
|
||||
if (start === end) {
|
||||
endCallback && endCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
let d = start + step > end ? end : start + step;
|
||||
if (start > end) {
|
||||
d = start - step < end ? end : start - step;
|
||||
}
|
||||
|
||||
if (el === window) {
|
||||
window.scrollTo(d, d);
|
||||
} else {
|
||||
el.scrollTop = d;
|
||||
}
|
||||
window.requestAnimationFrame(() => scroll(d, end, step));
|
||||
}
|
||||
scroll(from, to, step);
|
||||
}
|
||||
|
||||
export const sharpMatcherRegx = /#([^#]+)$/;
|
||||
|
||||
export const dimensionMap = {
|
||||
xs: "480px",
|
||||
sm: "576px",
|
||||
md: "768px",
|
||||
lg: "992px",
|
||||
xl: "1200px",
|
||||
xxl: "1600px"
|
||||
};
|
||||
|
||||
|
||||
/* istanbul ignore next */
|
||||
export const on = (function() {
|
||||
if (!isServer && document.addEventListener) {
|
||||
return function(element, event, handler, useCapture = false) {
|
||||
if (element && event && handler) {
|
||||
element.addEventListener(event, handler, useCapture);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return function(element, event, handler) {
|
||||
if (element && event && handler) {
|
||||
element.attachEvent("on" + event, handler);
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
/* istanbul ignore next */
|
||||
export const off = (function() {
|
||||
if (!isServer && document.removeEventListener) {
|
||||
return function(element, event, handler, useCapture = false) {
|
||||
if (element && event) {
|
||||
element.removeEventListener(event, handler, useCapture);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return function(element, event, handler) {
|
||||
if (element && event) {
|
||||
element.detachEvent("on" + event, handler);
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
@ -87,6 +87,9 @@
|
||||
<li v-if="tools.image" name="图片">
|
||||
<span @click="insertImage" class="iconfont icon-img"></span>
|
||||
</li>
|
||||
<li v-if="tools.uploadImage" name="本地图片">
|
||||
<span @click="chooseImage" class="iconfont icon-upload-img"></span>
|
||||
</li>
|
||||
<li v-if="tools.table" name="表格">
|
||||
<span @click="insertTable" class="iconfont icon-table"></span>
|
||||
</li>
|
||||
@ -190,6 +193,12 @@
|
||||
>
|
||||
<div v-html="html" ref="previewInner"></div>
|
||||
</div>
|
||||
<!-- 目录 -->
|
||||
<Anchor :offset-top="40" style="margin-left:20px;width:20%">
|
||||
<template v-for="item in toc">
|
||||
<AnchorLink :href="`#${item.anchor}`" :title="item.text" :key="item.anchor" />
|
||||
</template>
|
||||
</Anchor>
|
||||
</div>
|
||||
<!-- 预览图片-->
|
||||
<div :class="['preview-img', previewImgModal ? 'active' : '']">
|
||||
|
@ -7,7 +7,7 @@ import codemirrorConfig from '../../assets/js/codemirror/config';
|
||||
import '../../assets/js/codemirror/styles/codemirror.css';
|
||||
import common from '../../mixins/common';
|
||||
import marked from '../../config/marked';
|
||||
|
||||
import tocObj from "../../assets/js/marked/createToc";
|
||||
export default {
|
||||
name: 'markdown-pro',
|
||||
mixins: [common],
|
||||
@ -345,6 +345,10 @@ export default {
|
||||
html = html.replace(/<pre>/g, '<div class="code-block"><span class="copy-code">' + this.copyBtnText + '</span><pre>').replace(/<\/pre>/g, '</pre></div>')
|
||||
}
|
||||
this.html = html;
|
||||
//toc
|
||||
this.toc = tocObj.tocItems;
|
||||
tocObj.reset()
|
||||
|
||||
this.addImageClickListener();
|
||||
this.addCopyListener();
|
||||
this.$emit('input', currentValue);
|
||||
|
@ -85,6 +85,9 @@
|
||||
<li v-if="tools.image" name="图片">
|
||||
<span @click="insertImage" class="iconfont icon-img"></span>
|
||||
</li>
|
||||
<li v-if="tools.uploadImage" name="本地图片">
|
||||
<span @click="chooseImage" class="iconfont icon-upload-img"></span>
|
||||
</li>
|
||||
<li v-if="tools.table" name="表格">
|
||||
<span @click="insertTable" class="iconfont icon-table"></span>
|
||||
</li>
|
||||
|
@ -18,6 +18,7 @@ export default {
|
||||
code: true,
|
||||
link: true,
|
||||
image: true,
|
||||
uploadImage:false,
|
||||
table: true,
|
||||
checked: true,
|
||||
notChecked: true,
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { saveFile } from '../utils';
|
||||
import {saveFile} from '../utils';
|
||||
import defaultTools from '../config/tools';
|
||||
|
||||
import AnchorLink from '../components/Anchor/anchorLink'
|
||||
import Anchor from '../components/Anchor/anchor'
|
||||
export default {
|
||||
name: 'markdown',
|
||||
components: {Anchor,AnchorLink},
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number],
|
||||
@ -48,7 +51,7 @@ export default {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
copyCode:{// 复制代码
|
||||
copyCode: {// 复制代码
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
@ -75,6 +78,7 @@ export default {
|
||||
timerId: null, // 定时器id
|
||||
themeName: '',
|
||||
themeSlideDown: false,
|
||||
imgSlideDown: false,
|
||||
imgs: [],
|
||||
scrolling: true, // 同步滚动
|
||||
editorScrollHeight: 0,
|
||||
@ -85,7 +89,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
tools() {
|
||||
const { toolbars = {} } = this;
|
||||
const {toolbars = {}} = this;
|
||||
return {
|
||||
...defaultTools,
|
||||
...toolbars
|
||||
@ -105,7 +109,7 @@ export default {
|
||||
);
|
||||
},
|
||||
handleSave() {// 保存操作
|
||||
const { currentValue, themeName,html } = this;
|
||||
const {currentValue, themeName, html} = this;
|
||||
this.$emit('on-save', {
|
||||
theme: themeName,
|
||||
value: currentValue,
|
||||
@ -128,8 +132,8 @@ export default {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
const { type } = file;
|
||||
if (!['text/markdown','text/src'].includes(type)) {
|
||||
const {type} = file;
|
||||
if (!['text/markdown', 'text/src'].includes(type)) {
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
@ -139,17 +143,17 @@ export default {
|
||||
reader.onload = () => {
|
||||
this.currentValue = reader.result;
|
||||
e.target.value = '';
|
||||
if(this.pro){// 专业版,手动set value
|
||||
if (this.pro) {// 专业版,手动set value
|
||||
this.editor.setOption('value', this.currentValue);
|
||||
}
|
||||
};
|
||||
reader.onerror = err=>{
|
||||
reader.onerror = err => {
|
||||
console.error(err);
|
||||
}
|
||||
},
|
||||
handlePaste(_, e) {// 粘贴图片
|
||||
const { clipboardData = {} } = e;
|
||||
const { types = [], items } = clipboardData;
|
||||
const {clipboardData = {}} = e;
|
||||
const {types = [], items} = clipboardData;
|
||||
let item = null;
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
if (types[i] === 'Files') {
|
||||
@ -160,7 +164,7 @@ export default {
|
||||
if (item) {
|
||||
const file = item.getAsFile();
|
||||
if (/image/gi.test(file.type)) {
|
||||
this.$emit('on-paste-image', file);
|
||||
this.$emit('on-upload-image', file);
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
@ -169,7 +173,7 @@ export default {
|
||||
this.scrollSide = side;
|
||||
},
|
||||
addImageClickListener() {// 监听查看大图
|
||||
const { imgs = [] } = this;
|
||||
const {imgs = []} = this;
|
||||
if (imgs.length > 0) {
|
||||
for (let i = 0, len = imgs.length; i < len; i++) {
|
||||
imgs[i].onclick = null;
|
||||
@ -200,6 +204,19 @@ export default {
|
||||
this.previewImgModal = true;
|
||||
};
|
||||
},
|
||||
chooseImage() {// 选择图片
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'image/*';
|
||||
input.onchange = ()=>{
|
||||
const files = input.files;
|
||||
if(files[0]){
|
||||
this.$emit('on-upload-image', files[0]);
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
input.click();
|
||||
},
|
||||
addCopyListener() {// 监听复制操作
|
||||
setTimeout(() => {
|
||||
const btns = document.querySelectorAll(
|
||||
|
Loading…
Reference in New Issue
Block a user