引用图片
@@ -171,7 +169,8 @@ import Notify from '@renderer/scripts/notify'
import { useDraggable } from '@renderer/scripts/draggable'
import type { shortcutFunc } from '@renderer/scripts/shortcut-register'
import { treeToInfo, provideKeyDocInfo, provideKeyCurArticleInfo } from '@renderer/views/doc/doc'
-import { TempTextareaKey, ArticleReference, DocEditorStyle } from './scripts/article'
+import { TempTextareaKey, ArticleReference, DocEditorStyle, parseTocAsync } from './scripts/article'
+import type { Toc } from './scripts/article'
import { beforeUpload, onError, picCacheWrapper, picCacheRefresh, uploadForm, uploadDate } from '@renderer/views/picture/scripts/picture'
import { useResize } from './scripts/editor-preview-resize'
// codemirror
@@ -189,6 +188,8 @@ import marked, {
} from './scripts/markedjs'
import { EPScroll } from './scripts/editor-preview-scroll'
import { useArticleHtmlEvent } from './scripts/article-html-event'
+import { shallowRef } from 'vue'
+import { keymaps } from './scripts/editor-tools'
const PictureViewerInfo = defineAsyncComponent(() => import('@renderer/views/picture/PictureViewerInfo.vue'))
// const EditorTools = defineAsyncComponent(() => import('./EditorTools.vue'))
@@ -463,7 +464,7 @@ const saveCurArticleContent = async (auto: boolean = false) => {
name: curArticle.value!.name,
markdown: cmw.getDocString(),
html: PreviewRef.value.innerHTML,
- toc: JSON.stringify(articleToc.value),
+ // toc: JSON.stringify(articleToc.value),
references: articleImg.value.concat(articleLink.value)
}
await articleUpdContentApi(data)
@@ -614,7 +615,7 @@ const setNewState = (md: string): void => {
//#region ----------------------------------------< marked/preview >-------------------------------
const renderInterval = ref(0) // 解析用时
-const articleHtml = ref('') // 解析后的 html 内容
+const articleHtml = shallowRef('') // 解析后的 html 内容
let immediateParse = false // 是否立即渲染, 文档初次加载时立即渲染, 内容变更时防抖渲染
/**
* 自定义渲染
@@ -633,7 +634,6 @@ const renderer = {
return renderCode(code, language, _isEscaped)
},
heading(text: any, level: number): string {
- articleToc.value.push({ level: level, clazz: 'toc-' + level, index: articleToc.value.length, content: text })
return renderHeading(text, level)
},
image(href: string | null, _title: string | null, text: string): string {
@@ -671,6 +671,7 @@ const parse = () => {
articleHtml.value = content
renderInterval.value = Date.now() - begin
articleParseing = false
+ nextTick(() => parseToc())
})
}
@@ -693,9 +694,9 @@ useResize(EditorRef, PreviewRef, ResizeDividerRef)
//#endregion
//#region ----------------------------------------< TOC >------------------------------------------
-const articleToc = ref
([])
-const articleImg = ref([]) // 文章对图片引用
-const articleLink = ref([]) // 文章对链接的引用
+const articleToc = shallowRef([])
+const articleImg = shallowRef([]) // 文章对图片引用
+const articleLink = shallowRef([]) // 文章对链接的引用
const TocRef = ref()
const TocTitleRef = ref()
/**
@@ -703,18 +704,20 @@ const TocTitleRef = ref()
* @param level 标题级别
* @param content 标题内容
*/
-const toScroll = (level: number, content: string) => {
- let id = level + '-' + content
+const toScroll = (id: string) => {
let elm: HTMLElement = document.getElementById(id) as HTMLElement
;(elm.parentNode as Element).scrollTop = elm.offsetTop
}
// 清空当前目录内容
const clearTocAndImg = () => {
- articleToc.value = []
articleImg.value = []
articleLink.value = []
}
+const parseToc = async () => {
+ parseTocAsync(PreviewRef.value).then((tocs) => (articleToc.value = tocs))
+}
+
useDraggable(TocRef, TocTitleRef)
//#endregion
diff --git a/blossom-editor/src/renderer/src/views/article/ArticleViewWindow.vue b/blossom-editor/src/renderer/src/views/article/ArticleViewWindow.vue
index b285cd4..1d85251 100644
--- a/blossom-editor/src/renderer/src/views/article/ArticleViewWindow.vue
+++ b/blossom-editor/src/renderer/src/views/article/ArticleViewWindow.vue
@@ -4,60 +4,62 @@
《{{ article?.name }}》
- {{ article?.words }} 字 |
- {{ article?.uv }} |
+ {{ article?.words }} 字 | {{ article?.uv }} |
{{ article?.likes }}
-
- 公开 {{ article?.openTime }}
-
-
- 修改 {{ article?.updTime }}
-
+
公开 {{ article?.openTime }}
+
修改 {{ article?.updTime }}
目录
-
+
-
\ No newline at end of file
+
diff --git a/blossom-editor/src/renderer/src/views/article/EditorTools.vue b/blossom-editor/src/renderer/src/views/article/EditorTools.vue
index e1c2679..22c67ff 100644
--- a/blossom-editor/src/renderer/src/views/article/EditorTools.vue
+++ b/blossom-editor/src/renderer/src/views/article/EditorTools.vue
@@ -227,7 +227,7 @@
=> {
+ let heads = ele.querySelectorAll('h1, h2, h3, h4, h5, h6')
+ let tocs: Toc[] = []
+ for (let i = 0; i < heads.length; i++) {
+ let head: Element = heads[i]
+ let level = 1
+ let content = (head as HTMLElement).innerText
+ let id = head.id
+ switch (head.localName) {
+ case 'h1':
+ level = 1
+ break
+ case 'h2':
+ level = 2
+ break
+ case 'h3':
+ level = 3
+ break
+ case 'h4':
+ level = 4
+ break
+ case 'h5':
+ level = 5
+ break
+ case 'h6':
+ level = 6
+ break
+ }
+ let toc: Toc = { content: content, clazz: 'toc-' + level, id: id }
+ tocs.push(toc)
+ }
+ return tocs
+}
diff --git a/blossom-editor/src/renderer/src/views/article/scripts/markedjs.ts b/blossom-editor/src/renderer/src/views/article/scripts/markedjs.ts
index 3b3e5c8..593958c 100644
--- a/blossom-editor/src/renderer/src/views/article/scripts/markedjs.ts
+++ b/blossom-editor/src/renderer/src/views/article/scripts/markedjs.ts
@@ -103,15 +103,26 @@ export const tokenizerCodespan = (src: string): any => {
//#endregion
//#region ----------------------------------------< renderer >--------------------------------------
-
+const domParser = new DOMParser()
/**
* 标题解析为 TOC 集合, 增加锚点跳转
* @param text 标题内容
* @param level 标题级别
*/
export const renderHeading = (text: any, level: number) => {
- const realLevel = level
- return `${text}`
+ let id: string = randomInt(1000000, 9999999).toString()
+ try {
+ let dom = domParser.parseFromString(text, 'text/html')
+ if (dom) {
+ id += dom.body.innerText
+ } else {
+ id += text
+ }
+ } catch {
+ id += text
+ }
+
+ return `${text}`
}
/**
@@ -328,25 +339,10 @@ export const renderCode = (code: string, language: string | undefined, _isEscape
/**
* 单行代码块的解析拓展
- * 1. katex `$内部写表达式$`
* @param src
* @returns
*/
export const renderCodespan = (src: string) => {
- let arr = src.match(singleDollar)
- if (arr != null && arr.length > 0) {
- try {
- return katex.renderToString(arr[1], {
- throwOnError: true,
- output: 'html'
- })
- } catch (error) {
- console.error(error)
- return `
- Katex 语法解析失败! 你可以尝试前往
Katex 官网 来校验你的公式。
-
`
- }
- }
return `${src}`
}
diff --git a/blossom-editor/src/renderer/src/views/article/styles/bl-preview-toc.scss b/blossom-editor/src/renderer/src/views/article/styles/bl-preview-toc.scss
index 5025b97..ddbf359 100644
--- a/blossom-editor/src/renderer/src/views/article/styles/bl-preview-toc.scss
+++ b/blossom-editor/src/renderer/src/views/article/styles/bl-preview-toc.scss
@@ -7,7 +7,6 @@
.toc-content {
overflow-y: overlay;
- padding-top: 10px;
.toc-1,
.toc-2,
@@ -38,33 +37,23 @@
}
.toc-2 {
- &::before {
- content: ' ';
- }
+ padding-left: 10px;
}
.toc-3 {
- &::before {
- content: ' ';
- }
+ padding-left: 20px;
}
.toc-4 {
- &::before {
- content: ' ';
- }
+ padding-left: 30px;
}
.toc-5 {
- &::before {
- content: ' ';
- }
+ padding-left: 40px;
}
.toc-6 {
- &::before {
- content: ' ';
- }
+ padding-left: 50px;
}
}
}
diff --git a/blossom-web/src/views/article/Articles.vue b/blossom-web/src/views/article/Articles.vue
index 905b12e..91d7da3 100644
--- a/blossom-web/src/views/article/Articles.vue
+++ b/blossom-web/src/views/article/Articles.vue
@@ -113,7 +113,7 @@
-
@@ -185,7 +185,7 @@ const article = ref
({
html: `请在左侧菜单选择文章
`
})
-const tocList = ref([])
+const tocList = ref([])
const defaultOpeneds = ref([])
const PreviewRef = ref()
@@ -217,6 +217,10 @@ const getDocTree = () => {
}
}
+/**
+ * 获取文章信息
+ * @param tree
+ */
const clickCurDoc = async (tree: DocTree) => {
// 如果选中的是文章, 则查询文章详情, 用于在编辑器中显示以及注入
if (tree.ty == 3) {
@@ -224,6 +228,7 @@ const clickCurDoc = async (tree: DocTree) => {
window.history.replaceState('', '', '#/articles?articleId=' + tree.i)
nextTick(() => {
PreviewRef.value.scrollTo({ top: 0 })
+ parseTocAsync(PreviewRef.value)
})
}
}
@@ -240,19 +245,56 @@ const getCurEditArticle = async (id: number) => {
}
const then = (resp: any) => {
- if (isNull(resp.data)) return
+ if (isNull(resp.data)) {
+ return
+ }
article.value = resp.data
- tocList.value = JSON.parse(resp.data.toc)
}
if (userStore.isLogin) {
- await articleInfoApi({ id: id, showToc: true, showMarkdown: false, showHtml: true }).then((resp) => then(resp))
+ await articleInfoApi({ id: id, showToc: false, showMarkdown: false, showHtml: true }).then((resp) => then(resp))
} else {
- await articleInfoOpenApi({ id: id, showToc: true, showMarkdown: false, showHtml: true }).then((resp) => then(resp))
+ await articleInfoOpenApi({ id: id, showToc: false, showMarkdown: false, showHtml: true }).then((resp) => then(resp))
}
}
-const toScroll = (level: number, content: string) => {
- let id = level + '-' + content
+/**
+ * 解析目录
+ */
+const parseTocAsync = async (ele: HTMLElement) => {
+ let heads = ele.querySelectorAll('h1, h2, h3, h4, h5, h6')
+ let tocs: Toc[] = []
+ for (let i = 0; i < heads.length; i++) {
+ let head: Element = heads[i]
+ let level = 1
+ let content = (head as HTMLElement).innerText
+ let id = head.id
+ switch (head.localName) {
+ case 'h1':
+ level = 1
+ break
+ case 'h2':
+ level = 2
+ break
+ case 'h3':
+ level = 3
+ break
+ case 'h4':
+ level = 4
+ break
+ case 'h5':
+ level = 5
+ break
+ case 'h6':
+ level = 6
+ break
+ }
+ let toc: Toc = { content: content, clazz: 'toc-' + level, id: id }
+ tocs.push(toc)
+ }
+ tocList.value = tocs
+}
+
+const toScroll = (id: string) => {
let elm = document.getElementById(id)
elm?.scrollIntoView(true)
}
@@ -349,6 +391,9 @@ const closeAll = () => {
maskStyle.value = { display: 'none' }
}
+/**
+ *
+ */
const onresize = () => {
let width = document.body.clientWidth
if (width < 1100) {
@@ -552,10 +597,6 @@ const onresize = () => {
.toc-5,
.toc-6 {
cursor: pointer;
- // overflow: hidden;
- // white-space: nowrap;
- // text-overflow: ellipsis;
- // white-space: pre;
&:hover {
font-weight: bold;
@@ -564,7 +605,6 @@ const onresize = () => {
.toc-1 {
font-size: 1.1em;
- border-top: 2px solid #eeeeee;
margin-top: 5px;
padding-top: 5px;
diff --git a/blossom-web/src/views/article/index.d.ts b/blossom-web/src/views/article/index.d.ts
index b3144e2..986e50b 100644
--- a/blossom-web/src/views/article/index.d.ts
+++ b/blossom-web/src/views/article/index.d.ts
@@ -53,3 +53,12 @@ declare type DocType = 1 | 2 | 3
declare interface Window {
onHtmlEventDispatch: any
}
+
+/**
+ * 目录结构
+ */
+declare interface Toc {
+ content: string
+ clazz: string
+ id: string
+}