提交 4665e8e3 编写于 作者: 郭维嘉

feat:添加目录

上级 e52f5f9d
无法预览此类型文件
...@@ -28,7 +28,10 @@ ...@@ -28,7 +28,10 @@
<div id="app"></div> <div id="app"></div>
<!-- <textarea class="text" name="" id="" cols="30" rows="10"></textarea> --> <!-- <textarea class="text" name="" id="" cols="30" rows="10"></textarea> -->
<!-- <div contenteditable="true" class="text"></div> --> <!-- <div contenteditable="true" class="text"></div> -->
<a href="https://codechina.csdn.net/xiongjiamu/jupyter-101/-/blob/master/002-demo.ipynb" class="jupyterEl"></a> <a
href="https://codechina.csdn.net/xiongjiamu/jupyter-101/-/blob/master/002-demo.ipynb"
class="jupyterEl"
></a>
<button id="a">111111</button> <button id="a">111111</button>
<button id="b">2222222</button> <button id="b">2222222</button>
<script src="./markdown-editor.js"></script> <script src="./markdown-editor.js"></script>
...@@ -261,7 +264,27 @@ ...@@ -261,7 +264,27 @@
return ee.getVideoList(); return ee.getVideoList();
} }
document.querySelector("#a").onclick = async function() { document.querySelector("#a").onclick = async function() {
console.log(await test()); ee.setValue(`
# 一级标题
## 二级标题
### 三级标题
#### 四级标题
#### 四级标题2四级标题2四级标题2四级标题2四级标题2四级标题2四级标题2四级标题2
#### 四级标题3
#### 四级标题4
### 三级标题2
### 三级标题3
## 二级标题2
## 二级标题3
## 二级标题4
## 二级标题5
### 三级标题4
[toc]
`);
// ee.disable(); // ee.disable();
// ee.getVideoList(list => { // ee.getVideoList(list => {
......
此差异已折叠。
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
:registerTools="registerTools" :registerTools="registerTools"
@upload="handleUpload" @upload="handleUpload"
@getFormatType="formatType = $event" @getFormatType="formatType = $event"
@updateShowHelp="showHelp = $event" @updateShowDoc="showHelp = $event"
@renderLinks="$emit('renderLinks', $event)" @renderLinks="$emit('renderLinks', $event)"
/> />
<input <input
...@@ -41,6 +41,9 @@ ...@@ -41,6 +41,9 @@
:height="textareaHeight" :height="textareaHeight"
:html.sync="html" :html.sync="html"
:htmlMinHeight="htmlMinHeight" :htmlMinHeight="htmlMinHeight"
:show-help="showHelp"
:dirTags="dirTags"
@updateShowDoc="showHelp = $event"
v-if="showPreview" v-if="showPreview"
/> />
...@@ -69,8 +72,9 @@ ...@@ -69,8 +72,9 @@
@submit="submit" @submit="submit"
@enter="handleEnter" @enter="handleEnter"
@getFilteredTags="filteredTags = $event" @getFilteredTags="filteredTags = $event"
@updateShowHelp="showHelp = $event" @updateShowDoc="showHelp = $event"
@renderLinksHtml="renderLinksHtml" @renderLinksHtml="renderLinksHtml"
@getDirTags="getDirTags"
@queryUserList="queryUserList" @queryUserList="queryUserList"
@callUserList="callUserList = $event" @callUserList="callUserList = $event"
v-else v-else
...@@ -304,6 +308,7 @@ export default { ...@@ -304,6 +308,7 @@ export default {
uploadFlePercent: 0, uploadFlePercent: 0,
uploadVideoPercent: 0, uploadVideoPercent: 0,
textLength: "", textLength: "",
dirTags: [],
userList: false, userList: false,
callUserList: [], callUserList: [],
linkList: [], linkList: [],
...@@ -475,12 +480,36 @@ export default { ...@@ -475,12 +480,36 @@ export default {
} }
}); });
}, },
getDirTags({ vDom, dirTags }) {
this.dirTags = dirTags;
const tocEls = Array.from(vDom.querySelectorAll(".tocEl"));
if (!tocEls.length) return;
tocEls.forEach(el => {
el.innerHTML = `<ul class="md_toc_list">${dirTags
.map(tag => {
return `<li class="md_toc_item" data-type="${tag.tag}" data-id="${tag.id}">${tag.text}</li>`;
})
.join("")}</ul>`;
});
this.html = vDom.innerHTML;
// console.log(document.querySelectorAll(".md_toc_list"));
document.querySelector("body").addEventListener("click", function(e) {
if (!e.target?.className.includes("md_toc_item")) return;
console.log(e.target.dataset.id);
const targetEl = document.getElementById(e.target?.dataset?.id);
if (!targetEl) return;
targetEl.scrollIntoView({
behavior: "smooth"
});
});
},
renderLinksHtml({ vDom, links }) { renderLinksHtml({ vDom, links }) {
// 缓存里没有的链接,就发送请求获取信息 // 缓存里没有的链接,就发送请求获取信息
const emitList = links.filter( const emitList = links.filter(
item => !this.linkList.find(link => link && link.url === item.url) item => !this.linkList.find(link => link && link.url === item.url)
); );
console.log("emit", emitList);
this.$emit("renderLinks", { this.$emit("renderLinks", {
links: emitList, links: emitList,
callback: list => { callback: list => {
......
...@@ -183,6 +183,13 @@ export function isNotFalse(val) { ...@@ -183,6 +183,13 @@ export function isNotFalse(val) {
return val !== false; return val !== false;
} }
export function pick(list, ...arg) {
if (!list.length || !arg?.length) return;
return list.filter(item => {
return arg.find(key => key === item.name);
});
}
export function checktUrl(val, rule) { export function checktUrl(val, rule) {
if (!val || !rule) return; if (!val || !rule) return;
const hideEl = document.createElement("div"); const hideEl = document.createElement("div");
...@@ -360,7 +367,7 @@ export function formatElements(html) { ...@@ -360,7 +367,7 @@ export function formatElements(html) {
} }
}); });
Array.from(virtualDom.querySelectorAll("img")).forEach(item => { Array.from(virtualDom.querySelectorAll("img")).forEach(item => {
item.className = 'md_img' item.className = "md_img";
}); });
const list = Array.from(new Set(userList)); // 去重 const list = Array.from(new Set(userList)); // 去重
return { callUserList: list, userHtml: virtualDom.innerHTML }; return { callUserList: list, userHtml: virtualDom.innerHTML };
...@@ -368,6 +375,7 @@ export function formatElements(html) { ...@@ -368,6 +375,7 @@ export function formatElements(html) {
export function getLinkTags(id, html) { export function getLinkTags(id, html) {
const virtualDom = document.createElement("div"); const virtualDom = document.createElement("div");
virtualDom.innerHTML = html; virtualDom.innerHTML = html;
// 获取所有a标签
const links = Array.from( const links = Array.from(
virtualDom.querySelectorAll("a:not([download])") virtualDom.querySelectorAll("a:not([download])")
).map((item, index) => { ).map((item, index) => {
...@@ -378,7 +386,18 @@ export function getLinkTags(id, html) { ...@@ -378,7 +386,18 @@ export function getLinkTags(id, html) {
url: item.href url: item.href
}; };
}); });
return { vDom: virtualDom, links }; // 获取所有标题
const dirTags = Array.from(
virtualDom.querySelectorAll("h1:not(.toc_title),h2,h3,h4,h5,h6")
).map((item, index) => {
const dirItem = {
tag: item.tagName.toLowerCase(),
id: item.id,
text: item.innerText
};
return dirItem;
});
return { vDom: virtualDom, links, dirTags };
} }
export function getLinkTitle(linkEl, item) { export function getLinkTitle(linkEl, item) {
......
...@@ -86,6 +86,46 @@ textarea { ...@@ -86,6 +86,46 @@ textarea {
} }
} }
.md_toc_list {
li.md_toc_item {
list-style-type: none;
cursor: pointer;
color: var(--md-editor-border-color-active);
position: relative;
margin: 12px 8px;
&::before {
content: "\·";
font-size: 20px;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
}
&[data-type="h1"] {
padding-left: 20px;
}
&[data-type="h2"] {
padding-left: 30px;
}
&[data-type="h3"] {
padding-left: 40px;
}
&[data-type="h4"] {
padding-left: 50px;
}
&[data-type="h5"] {
padding-left: 60px;
}
&[data-type="h6"] {
padding-left: 70px;
}
}
}
.relative {
position: relative;
}
a.md_call_user { a.md_call_user {
color: var(--md-editor-text-color-active) !important; color: var(--md-editor-text-color-active) !important;
&[href] { &[href] {
......
...@@ -15,6 +15,10 @@ ...@@ -15,6 +15,10 @@
content: "\e63a"; content: "\e63a";
} }
.icon-dir:before {
content: "\e67f";
}
.icon-renwu:before { .icon-renwu:before {
content: "\e63f"; content: "\e63f";
} }
...@@ -84,7 +88,7 @@ ...@@ -84,7 +88,7 @@
} }
.icon-aite:before { .icon-aite:before {
content: "\e634"; content: "\e60b";
} }
.icon-fengexian:before { .icon-fengexian:before {
content: "\e60a"; content: "\e60a";
......
<template>
<div class="doc_frame">
<div class="doc_container">
<h2>
<slot name="title" />
<span
@click="$emit('updateShowDoc', false)"
:class="['icon iconfont', `icon-guanbi`]"
></span>
</h2>
<slot name="content" />
<!-- <ul class="list">
<li v-for="(item, index) in list" :key="index">
<span :class="['icon iconfont', `icon-${item.icon}`]"></span>
<span>{{ item.title }}</span>
<span class="doc">{{ item.doc }}</span>
</li>
</ul> -->
</div>
<div class="before"></div>
<div class="after"></div>
</div>
</template>
<script>
export default {
props: {
showHelp: {
type: [Boolean, String],
default: false
}
},
data() {
return {
list: [
{
title: "一级标题",
doc: "# 标题",
icon: "yijibiaoti"
},
{
title: "二级标题",
doc: "## 标题",
icon: "erjibiaoti"
},
{
title: "三级标题",
doc: "### 标题",
icon: "sanjibiaoti"
},
{
title: "粗体",
doc: "**内容**",
icon: "bold"
},
{
title: "斜体",
doc: "_内容_",
icon: "italic"
},
{
title: "引用",
doc: "> 引用内容",
icon: "yinyong"
},
{
title: "链接",
doc: "[链接标题](url)",
icon: "lianjie"
},
{
title: "图片",
doc: "![alt](url)",
icon: "img"
},
{
title: "图片大小",
doc: '![alt](url "=300x200")',
icon: "img"
},
{
title: "图片位置",
doc: '![alt](url "#left")',
icon: "img"
},
{
title: "图片名称",
doc: '![alt](url "%title")',
icon: "img"
},
{
title: "代码",
doc: "`代码`",
icon: "daimakuai"
},
{
title: "代码块",
doc: "```编程语言↵代码```",
icon: "code"
},
{
title: "无序列表",
doc: "- 内容",
icon: "unorderedList"
},
{
title: "有序列表",
doc: "1. 内容",
icon: "youxuliebiao"
},
{
title: "任务列表",
doc: "- [ ] 待办事项",
icon: "renwu"
},
{
title: "分割线",
doc: "---",
icon: "fengexian"
},
{
title: "删除线",
doc: "~~内容~~",
icon: "shanchuxian"
}
]
};
}
};
</script>
<style lang="less" scoped>
.doc_frame {
position: absolute;
// background: #fff;
background: var(--md-editor-content-bg-color);
height: calc(100% + 10px);
width: 260px;
top: 0;
right: -16px;
box-sizing: border-box;
border-left: 1px solid var(--md-editor-border-color);
padding: 14px;
padding-right: 0;
// border-radius: 4px;
z-index: 9;
.before {
position: absolute;
top: 4px;
left: 0;
width: 100%;
height: 24px;
pointer-events: none;
background: linear-gradient(
to bottom,
var(--md-editor-content-bg-color),
rgba(255, 255, 255, 0)
);
}
.after {
pointer-events: none;
position: absolute;
bottom: 12px;
left: 0;
width: 100%;
height: 24px;
background: linear-gradient(
to top,
var(--md-editor-content-bg-color),
rgba(255, 255, 255, 0)
);
}
.doc_container {
overflow-y: auto;
padding-right: 14px;
height: 100%;
scrollbar-color: transparent transparent;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
h2 {
font-size: 16px;
color: var(--md-editor-text-color-active);
.icon {
float: right;
margin-top: 3px;
cursor: pointer;
font-weight: 400;
}
}
/deep/ul {
margin-top: 14px;
li {
font-size: 14px;
color: var(--md-editor-helpdoc-color);
margin-bottom: 10px;
.icon {
display: inline-block;
vertical-align: middle;
}
.doc {
float: right;
}
}
}
}
}
</style>
<template> <template>
<div class="help_doc"> <docFrame @updateShowDoc="$emit('updateShowDoc', $event)">
<div class="doc_container"> <template #title> Markdown 语法 </template>
<h2> <template #content>
Markdown 语法 <ul>
<span
@click="$emit('updateShowHelp', false)"
:class="['icon iconfont', `icon-guanbi`]"
></span>
</h2>
<ul class="list">
<li v-for="(item, index) in list" :key="index"> <li v-for="(item, index) in list" :key="index">
<span :class="['icon iconfont', `icon-${item.icon}`]"></span> <span :class="['icon iconfont', `icon-${item.icon}`]"></span>
<span>{{ item.title }}</span> <span>{{ item.title }}</span>
<span class="doc">{{ item.doc }}</span> <span class="doc">{{ item.doc }}</span>
</li> </li>
</ul> </ul>
</div> </template>
<div class="before"></div> </docFrame>
<div class="after"></div>
</div>
</template> </template>
<script> <script>
import docFrame from "./doc-frame.vue";
export default { export default {
components: { docFrame },
props: { props: {
showHelp: { showHelp: {
type: Boolean, type: [Boolean, String],
default: false default: false
} }
}, },
...@@ -126,81 +120,3 @@ export default { ...@@ -126,81 +120,3 @@ export default {
} }
}; };
</script> </script>
<style lang="less" scoped>
.help_doc {
position: absolute;
// background: #fff;
background: var(--md-editor-content-bg-color);
height: calc(100% + 10px);
width: 260px;
top: 0;
right: -16px;
box-sizing: border-box;
border-left: 1px solid var(--md-editor-border-color);
padding: 14px;
padding-right: 0;
// border-radius: 4px;
z-index: 9;
.before {
position: absolute;
top: 4px;
left: 0;
width: 100%;
height: 24px;
pointer-events: none;
background: linear-gradient(
to bottom,
var(--md-editor-content-bg-color),
rgba(255, 255, 255, 0)
);
}
.after {
pointer-events: none;
position: absolute;
bottom: 12px;
left: 0;
width: 100%;
height: 24px;
background: linear-gradient(
to top,
var(--md-editor-content-bg-color),
rgba(255, 255, 255, 0)
);
}
.doc_container {
overflow-y: auto;
padding-right: 14px;
height: 100%;
scrollbar-color: transparent transparent;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
h2 {
font-size: 16px;
color: var(--md-editor-text-color-active);
.icon {
float: right;
margin-top: 3px;
cursor: pointer;
font-weight: 400;
}
}
ul.list {
margin-top: 14px;
li {
font-size: 14px;
color: var(--md-editor-helpdoc-color);
margin-bottom: 10px;
.icon {
display: inline-block;
vertical-align: middle;
}
.doc {
float: right;
}
}
}
}
}
</style>
<template>
<docFrame @updateShowDoc="$emit('updateShowDoc', $event)">
<template #title>目录</template>
<template #content>
<ul>
<li
:data-type="item.tag"
@click.prevent.stop="scrollToTitle(item)"
:class="{ active: dirItemActive(item) }"
v-for="item in dirTags"
:key="item.id"
>
<a href="javascript:viod(0)">
{{ item.text }}
</a>
</li>
</ul>
</template>
</docFrame>
</template>
<script>
import docFrame from "./doc-frame.vue";
export default {
components: { docFrame },
props: {
showHelp: {
type: [Boolean, String],
default: false
},
dirTags: {
type: Array,
default: () => []
},
scrollBarTop: {
type: Number,
default: 0
}
},
data() {
return {
list: [
{
title: "一级标题",
doc: "# 标题",
icon: "yijibiaoti"
},
{
title: "二级标题",
doc: "## 标题",
icon: "erjibiaoti"
},
{
title: "三级标题",
doc: "### 标题",
icon: "sanjibiaoti"
}
]
};
},
computed: {
topList() {
return this.dirTags.map(item => Math.abs(item?.top));
}
},
created() {
this.$emit(
"update:scrollBarTop",
document.querySelector(".md_preview_scroll_container").scrollTop
);
},
methods: {
scrollToTitle(item) {
const targetEl = document.getElementById(item.id);
if (!targetEl) return;
const targetOffsetTop = targetEl.offsetTop;
document.querySelector(
".md_preview .md_preview_scroll_container"
).scrollTop = targetOffsetTop;
},
dirItemActive(item) {
const itemScrollTop = document.getElementById(item.id)?.offsetTop;
const top = this.scrollBarTop - itemScrollTop;
this.$set(item, "top", top);
return Math.abs(top) === Math.min(...this.topList);
}
}
};
</script>
<style lang="less" scoped>
/deep/li {
position: relative;
cursor: pointer;
a {
text-decoration: none;
color: inherit;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
&:hover,
&.active {
a {
color: var(--md-editor-border-color-active);
}
&::before {
color: var(--md-editor-border-color-active);
}
}
&::before {
content: "\·";
position: absolute;
left: 0;
top: 50%;
font-size: 20px;
transform: translateY(-50%);
}
&[data-type="h1"] {
padding-left: 20px;
}
&[data-type="h2"] {
padding-left: 30px;
}
&[data-type="h3"] {
padding-left: 40px;
}
&[data-type="h4"] {
padding-left: 50px;
}
&[data-type="h5"] {
padding-left: 60px;
}
&[data-type="h6"] {
padding-left: 70px;
}
}
</style>
<template> <template>
<div :class="['md_preview', { fullScreen }]"> <div class="relative">
<div <div :class="['md_preview', { fullScreen }]">
v-html="html" <div
:style="{ class="md_preview_scroll_container"
height: height > 0 ? height + 'px' : 'auto', v-html="html"
'min-height': htmlMinHeight + 'px' @scroll="getScrollBarTop"
}" :style="{
></div> height: height > 0 ? height + 'px' : 'auto',
'min-height': htmlMinHeight + 'px'
}"
></div>
</div>
<transition name="slide-fade">
<helpDoc
v-if="showHelp === 'help'"
@updateShowDoc="$emit('updateShowDoc', $event)"
:showHelp.sync="showHelp"
/>
<dirDoc
v-if="showHelp === 'dir'"
@updateShowDoc="$emit('updateShowDoc', $event)"
:showHelp.sync="showHelp"
:dirTags="dirTags"
:scrollBarTop.sync="scrollBarTop"
/>
</transition>
</div> </div>
</template> </template>
<script> <script>
import helpDoc from "./components/help-doc.vue";
import dirDoc from "./components/toc-doc.vue";
export default { export default {
components: { helpDoc, dirDoc },
data() { data() {
return {}; return {
scrollBarTop: 0
};
}, },
props: { props: {
id: { id: {
...@@ -34,6 +57,14 @@ export default { ...@@ -34,6 +57,14 @@ export default {
type: [String, Promise], type: [String, Promise],
default: "" default: ""
}, },
showHelp: {
type: [Boolean, String],
default: false
},
dirTags: {
type: Array,
default: () => []
},
fullScreen: { fullScreen: {
type: Boolean, type: Boolean,
default: false default: false
...@@ -65,6 +96,12 @@ export default { ...@@ -65,6 +96,12 @@ export default {
item.setAttribute("preload", "auto"); item.setAttribute("preload", "auto");
}); });
}, 0); }, 0);
},
getScrollBarTop() {
if (this.showHelp === "dir")
this.scrollBarTop = document.querySelector(
".md_preview_scroll_container"
).scrollTop;
} }
} }
}; };
...@@ -77,12 +114,17 @@ export default { ...@@ -77,12 +114,17 @@ export default {
color: var(--md-editor-text-color); color: var(--md-editor-text-color);
word-break: break-all; word-break: break-all;
overflow-y: auto; overflow-y: auto;
& > div { .md_preview_scroll_container {
overflow-y: auto; overflow-y: auto;
position: relative;
scroll-behavior: smooth;
} }
&.fullScreen { &.fullScreen {
max-height: calc(100% - 42px); max-height: calc(100% - 42px);
overflow-y: auto; overflow-y: auto;
} }
.help_doc {
height: 100%;
}
} }
</style> </style>
...@@ -39,8 +39,13 @@ ...@@ -39,8 +39,13 @@
</textarea> </textarea>
<transition name="slide-fade"> <transition name="slide-fade">
<helpDoc <helpDoc
v-if="showHelp" v-if="showHelp === 'help'"
@updateShowHelp="$emit('updateShowHelp', $event)" @updateShowDoc="$emit('updateShowDoc', $event)"
:showHelp.sync="showHelp"
/>
<dirDoc
v-if="showHelp === 'dir'"
@updateShowDoc="$emit('updateShowDoc', $event)"
:showHelp.sync="showHelp" :showHelp.sync="showHelp"
/> />
</transition> </transition>
...@@ -73,11 +78,12 @@ import { ...@@ -73,11 +78,12 @@ import {
import selectUser from "./components/user-select.vue"; import selectUser from "./components/user-select.vue";
import selectLinkType from "./components/link-type-select.vue"; import selectLinkType from "./components/link-type-select.vue";
import helpDoc from "./components/help-doc.vue"; import helpDoc from "./components/help-doc.vue";
import dirDoc from "./components/toc-doc.vue";
import renderMix from "./mixins/render-mixins"; import renderMix from "./mixins/render-mixins";
import selectUserMix from "./mixins/select-user-mixins"; import selectUserMix from "./mixins/select-user-mixins";
import selectLinkTypeMix from "./mixins/select-link-type-mixins"; import selectLinkTypeMix from "./mixins/select-link-type-mixins";
export default { export default {
components: { helpDoc, selectUser, selectLinkType }, components: { helpDoc, dirDoc, selectUser, selectLinkType },
mixins: [renderMix, selectUserMix, selectLinkTypeMix], mixins: [renderMix, selectUserMix, selectLinkTypeMix],
props: { props: {
id: { id: {
...@@ -139,7 +145,7 @@ export default { ...@@ -139,7 +145,7 @@ export default {
default: "" default: ""
}, },
showHelp: { showHelp: {
type: Boolean, type: [Boolean, String],
default: false default: false
}, },
userList: { userList: {
......
...@@ -37,13 +37,15 @@ export default { ...@@ -37,13 +37,15 @@ export default {
}); // 去除标签 }); // 去除标签
const filteredTags = getFilteredTags(html, cleanHtml); // 计算是否有标签被过滤 const filteredTags = getFilteredTags(html, cleanHtml); // 计算是否有标签被过滤
// 链接转换为卡片 // 链接转换为卡片
const { vDom, links } = getLinkTags(this.id, cleanHtml); // 获取标题列表
const { vDom, links, dirTags } = getLinkTags(this.id, cleanHtml);
const { callUserList, userHtml } = formatElements(cleanHtml); const { callUserList, userHtml } = formatElements(cleanHtml);
// const videoHtml = await renderVideo(this.id, userHtml); // const videoHtml = await renderVideo(this.id, userHtml);
this.$emit("callUserList", callUserList); this.$emit("callUserList", callUserList);
this.$emit("getFilteredTags", filteredTags); this.$emit("getFilteredTags", filteredTags);
this.$emit("update:html", userHtml); this.$emit("update:html", userHtml);
if (links.length) this.$emit("renderLinksHtml", { vDom, links }); if (links.length) this.$emit("renderLinksHtml", { vDom, links });
if (dirTags.length) this.$emit("getDirTags", { vDom, dirTags });
}, },
rerender() { rerender() {
const _this = this; const _this = this;
...@@ -79,6 +81,8 @@ export default { ...@@ -79,6 +81,8 @@ export default {
src="${href}" src="${href}"
></video></p>`; ></video></p>`;
} }
console.log("imgimgimg");
// ![img](...)渲染图片 // ![img](...)渲染图片
let out = let out =
'<p class="md_img_container"><img src="' + '<p class="md_img_container"><img src="' +
...@@ -116,7 +120,37 @@ export default { ...@@ -116,7 +120,37 @@ export default {
out += "/></p>"; out += "/></p>";
return out; return out;
}, },
heading(text, level, raw, slugger) {
if (this.options.headerIds) {
return (
"<h" +
level +
' id="' +
"h" +
level +
"_" +
this.options.headerPrefix +
slugger.slug(raw) +
"_" +
new Date().getTime() +
'">' +
text +
"</h" +
level +
">\n"
);
}
// ignore IDs
return "<h" + level + ">" + text + "</h" + level + ">\n";
},
link(href, title, text) { link(href, title, text) {
console.log("linklink");
if (text?.toLowerCase() === "toc") {
return `
<h1 class="toc_title">${href}</h1>
<div class="tocEl"></div>`;
}
if (!href && !title) return ""; if (!href && !title) return "";
if (href === null) { if (href === null) {
return text; return text;
...@@ -155,9 +189,15 @@ export default { ...@@ -155,9 +189,15 @@ export default {
return `<a type="user" download data-user="${user && return `<a type="user" download data-user="${user &&
user.username}" href="${(user && user.url) || "javascript:void(0)"}" class="md_call_user">${val}</a>`; user.username}" href="${(user && user.url) || "javascript:void(0)"}" class="md_call_user">${val}</a>`;
}) })
// tab缩进
.replace(/^\s{2,3}(.+)/, function(val) { .replace(/^\s{2,3}(.+)/, function(val) {
return `<span style="display:inline-block;text-indent:2em;">${val}</span>`; return `<span style="display:inline-block;text-indent:2em;">${val}</span>`;
}); })
.replace(
/\[TOC\]/i,
`<h1 class="toc_title">目录</h1><div class="tocEl"></div>`
);
return newText; return newText;
} }
}; };
......
...@@ -265,7 +265,10 @@ export default { ...@@ -265,7 +265,10 @@ export default {
this.$emit("setFullScreen", false); this.$emit("setFullScreen", false);
break; break;
case "help": case "help":
this.$emit("updateShowHelp", true); this.$emit("updateShowDoc", "help");
break;
case "dir":
this.$emit("updateShowDoc", "dir");
break; break;
default: default:
break; break;
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<span>预览</span> <span>预览</span>
</div> </div>
</div> </div>
<div :class="['header_tools', { disabled }]" v-if="!showPreview"> <div :class="['header_tools', { disabled }]">
<tool-button <tool-button
:ref="item.name" :ref="item.name"
:ulNum.sync="ulNum" :ulNum.sync="ulNum"
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
@updateText="handleUpdateText" @updateText="handleUpdateText"
@upload="$emit('upload', $event)" @upload="$emit('upload', $event)"
@setFormatType="setFormatType" @setFormatType="setFormatType"
@updateShowHelp="$emit('updateShowHelp', $event)" @updateShowDoc="$emit('updateShowDoc', $event)"
:class="{ active: item.name === 'format' && formatType }" :class="{ active: item.name === 'format' && formatType }"
v-for="(item, index) in toolsShow" v-for="(item, index) in toolsShow"
:key="index" :key="index"
...@@ -47,7 +47,8 @@ import { ...@@ -47,7 +47,8 @@ import {
getPosition, getPosition,
removeBlankLine, removeBlankLine,
copyFormatRules, copyFormatRules,
checkBoswer checkBoswer,
pick
} from "@/assets/js/utils"; } from "@/assets/js/utils";
import toolButton from "./components/tool-button"; import toolButton from "./components/tool-button";
export default { export default {
...@@ -107,10 +108,18 @@ export default { ...@@ -107,10 +108,18 @@ export default {
} }
}, },
computed: { computed: {
previewTools() {
const list = this.toolButtonList;
return [
this.dirBtn,
...pick(list, "help", "fullScreen", "cancelFullScreen")
];
},
toolsShow() { toolsShow() {
const toolsList = this.toolButtonList; const toolsList = this.toolButtonList;
const toolsOptions = this.toolsOptions; const toolsOptions = this.toolsOptions;
if (!toolsOptions) return toolsList; if (!toolsOptions) return toolsList;
if (this.showPreview) return this.previewTools;
return toolsList.filter(item => { return toolsList.filter(item => {
return isNotFalse(toolsOptions[item.name]); return isNotFalse(toolsOptions[item.name]);
}); });
...@@ -179,6 +188,11 @@ export default { ...@@ -179,6 +188,11 @@ export default {
icon: "fullScreen", icon: "fullScreen",
tip: "全屏模式" tip: "全屏模式"
}, },
dirBtn: {
name: "dir",
icon: "dir",
tip: "目录"
},
toolButtonList: [ toolButtonList: [
{ {
name: "call", name: "call",
...@@ -298,6 +312,11 @@ export default { ...@@ -298,6 +312,11 @@ export default {
"\n\n| 表头 | 表头 |\n| ------ | ------ |\n| 单元格 | 单元格 |\n| 单元格 | 单元格 |\n\n", "\n\n| 表头 | 表头 |\n| ------ | ------ |\n| 单元格 | 单元格 |\n| 单元格 | 单元格 |\n\n",
endStr: "" endStr: ""
}, },
// {
// name: "dir",
// icon: "dir",
// tip: "目录"
// },
{ {
name: "help", name: "help",
icon: "help", icon: "help",
...@@ -408,7 +427,7 @@ export default { ...@@ -408,7 +427,7 @@ export default {
this.updateText(newText, len); this.updateText(newText, len);
if (startStr === "@") { if (startStr === "@") {
setTimeout(() => { setTimeout(() => {
this.$parent.$refs["md_" + this.id].createSelectUserDialog('android'); this.$parent.$refs["md_" + this.id].createSelectUserDialog("android");
}, 200); }, 200);
} }
}, },
......
...@@ -4,8 +4,8 @@ import Vtip from "vtip"; ...@@ -4,8 +4,8 @@ import Vtip from "vtip";
import "vtip/lib/index.min.css"; import "vtip/lib/index.min.css";
import { initStyle, setzIndex, isNotEmpty } from "@/assets/js/utils"; import { initStyle, setzIndex, isNotEmpty } from "@/assets/js/utils";
import "@/assets/style/global.less"; import "@/assets/style/global.less";
import "@/assets/jupyter-render/dist/index.js"; // import "@/assets/jupyter-render/dist/index.js";
import "@/assets/jupyter-render/dist/assets/index.css"; // import "@/assets/jupyter-render/dist/assets/index.css";
Vue.use(Vtip.directive); Vue.use(Vtip.directive);
function initMdEditor(obj) { function initMdEditor(obj) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册