光标处理未完成
This commit is contained in:
parent
15779301fa
commit
d08c85e70a
9
assets/workers/example.worker.js
Normal file
9
assets/workers/example.worker.js
Normal file
@ -0,0 +1,9 @@
|
||||
addEventListener('message', (e) => {
|
||||
const result = heavyTask(e.data)
|
||||
postMessage(result)
|
||||
})
|
||||
postMessage('Hello World!ABC')
|
||||
function heavyTask(data) {
|
||||
// 执行耗时计算
|
||||
return data * 2
|
||||
}
|
@ -3,13 +3,14 @@ import {Marked} from 'marked';
|
||||
// 默认导入所有语言
|
||||
import hljs from 'highlight.js';
|
||||
import 'highlight.js/styles/atom-one-light.css';
|
||||
// 注册语言
|
||||
// import hljs from 'highlight.js/lib/core';
|
||||
// import javascript from 'highlight.js/lib/languages/javascript';
|
||||
// hljs.registerLanguage('javascript', javascript);
|
||||
import DOMPurify from 'dompurify';
|
||||
import markedFootnote from 'marked-footnote'
|
||||
import markedCodeFormat from 'marked-code-format'
|
||||
import { markedEmoji } from 'marked-emoji';
|
||||
|
||||
const marked = new Marked();
|
||||
|
||||
|
||||
// 自定义行号注入函数
|
||||
const injectLineNumbers = (highlightedCode: string) => {
|
||||
const lines = highlightedCode.split('\n')
|
||||
@ -25,24 +26,93 @@ marked.use({
|
||||
async: true,
|
||||
pedantic: false,
|
||||
gfm: true,
|
||||
silent: true,
|
||||
silent: false,
|
||||
breaks: false,
|
||||
renderer: {
|
||||
code: ({text, lang}) => {
|
||||
console.log(text);
|
||||
// console.log(text);
|
||||
const validLang = hljs.getLanguage(lang) ? lang : 'plaintext'
|
||||
const highlighted = hljs.highlight(text, {language: lang}).value
|
||||
console.log(highlighted)
|
||||
// console.log(highlighted)
|
||||
const withLineNumbers = injectLineNumbers(highlighted)
|
||||
return `<pre class="hljs ${validLang}"><code>${withLineNumbers}</code></pre>`;
|
||||
}
|
||||
}
|
||||
})
|
||||
// 支持脚注[^1] 会影响原始文本
|
||||
marked.use(markedFootnote())
|
||||
marked.use(markedCodeFormat({
|
||||
/* Prettier options */
|
||||
}))
|
||||
|
||||
const markedEmojiOptions = {
|
||||
emojis: {
|
||||
"heart": "❤️",
|
||||
"tada": "🎉"
|
||||
},
|
||||
renderer: (token) => {
|
||||
console.log(token)
|
||||
return token.emoji
|
||||
}
|
||||
};
|
||||
marked.use(markedEmoji(markedEmojiOptions))
|
||||
|
||||
const content = ref(`
|
||||
# Hello Vue 3!
|
||||
|
||||
AAA
|
||||
AAA
|
||||
|
||||
BBB
|
||||
B
|
||||
|
||||
Hello :smile:
|
||||
|
||||
- [ ] xsxs
|
||||
|
||||
I :heart: marked! :tada:
|
||||
|
||||
## 脚注
|
||||
|
||||
这是标记[^1]
|
||||
|
||||
[^1]: This is a footnote content.
|
||||
|
||||
Here is a simple footnote[^1]. With some additional[^3] text after it[^@#$%] and without disrupting the blocks[^3].
|
||||
|
||||
[^2]: The first paragraph of the definition2.
|
||||
|
||||
[^3]: The first paragraph of the definition3.
|
||||
|
||||
[^4]: ABC
|
||||
|
||||
**Markdown 内容示例:**
|
||||
|
||||
1. 潇洒些
|
||||
2. 下洒下阿萨[^4]
|
||||
|
||||
3. 想啊伤心啊伤心啊
|
||||
1. 下洒下阿萨
|
||||
2. 下洒下阿萨
|
||||
a. sxaas
|
||||
b. xsaxa
|
||||
c. xsaxa
|
||||
- xasx
|
||||
- cascade
|
||||
|
||||
1. 潇洒些
|
||||
2. 下洒下阿萨
|
||||
3. 想啊伤心啊伤心啊
|
||||
1. 下洒下阿萨
|
||||
2. 下洒下阿萨
|
||||
a. sxaas
|
||||
b. xsaxa
|
||||
c. xsaxa
|
||||
- xasx
|
||||
- cascade
|
||||
|
||||
> 引用
|
||||
|
||||
- 列表项
|
||||
- 另一项
|
||||
|
||||
@ -63,19 +133,95 @@ function greet() {
|
||||
| sanitizer\t | function | \t自定义 HTML 过滤函数 | \t- |
|
||||
| silent\t | boolean | \t静默模式:忽略解析错误(如未闭合的代码块) | \tfalse |
|
||||
`);
|
||||
// 使用编译
|
||||
async function make(contents: string){
|
||||
// 过滤不能识别,难识别的字符,空字符
|
||||
let content = contents.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,"");
|
||||
// 编译
|
||||
content = await marked.parse(content)
|
||||
// 加强安全,防止xss攻击
|
||||
content = DOMPurify.sanitize(content);
|
||||
return content
|
||||
}
|
||||
|
||||
const compiledMarkdown = ref(await marked.parse(content.value));
|
||||
const compiledMarkdown = ref(await make(content.value));
|
||||
|
||||
const editableDiv = ref(null); // 绑定可编辑的 div
|
||||
const rawText = ref(""); // 存储纯文本内容
|
||||
|
||||
// 实时监听输入事件
|
||||
const handleInput = async (event) => {
|
||||
if (editableDiv.value) {
|
||||
// 直接获取去标签后的纯文本
|
||||
rawText.value = editableDiv.value.innerText.trim().replace(/ /g, ' ');
|
||||
console.log(rawText.value)
|
||||
compiledMarkdown.value = await make(rawText.value);
|
||||
}
|
||||
};
|
||||
|
||||
// 手动获取内容(通过按钮触发)
|
||||
const getContent = () => {
|
||||
if (editableDiv.value) {
|
||||
const text = editableDiv.value.innerText.trim();
|
||||
console.log("手动获取:", text);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理粘贴内容(自动去除粘贴的 HTML 标签)
|
||||
const handlePaste = (event) => {
|
||||
event.preventDefault(); // 阻止默认粘贴行为
|
||||
const text = event.clipboardData.getData("text/plain"); // 获取纯文本
|
||||
document.execCommand("insertText", false, text); // 插入纯文本
|
||||
};
|
||||
onMounted(async () => {
|
||||
|
||||
const worker = new Worker(new URL("~/assets/workers/example.worker.js", import.meta.url));
|
||||
worker.postMessage("Hello Worker");
|
||||
|
||||
worker.onmessage = (e) => {
|
||||
console.log(e)
|
||||
worker.terminate();
|
||||
};
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="NiMarked">
|
||||
<div class="inputMarkdown"
|
||||
ref="editableDiv"
|
||||
contenteditable="true"
|
||||
@input="handleInput"
|
||||
@paste="handlePaste"
|
||||
></div>
|
||||
<div
|
||||
ref="markdownContainer"
|
||||
class="markdown-content"
|
||||
v-html="compiledMarkdown"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.NiMarked{
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.NiMarked>div{
|
||||
outline: none;
|
||||
border: 1px solid var(--Ni-theme-border-color);
|
||||
}
|
||||
.inputMarkdown{
|
||||
position: relative;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
.markdown-content {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
// 基础样式...
|
||||
|
||||
/* 代码块样式 */
|
||||
|
222
components/Ni/Markdown.vue
Normal file
222
components/Ni/Markdown.vue
Normal file
@ -0,0 +1,222 @@
|
||||
<script setup lang="ts">
|
||||
import 'highlight.js/styles/atom-one-light.css';
|
||||
import DOMPurify from "dompurify";
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
contentId: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
})
|
||||
|
||||
// 原始内容
|
||||
const originContent = ref(props.content);
|
||||
// 编辑器内容
|
||||
const editorContent = ref('');
|
||||
// 显示器内容
|
||||
const viewerContent = ref('');
|
||||
// 编辑器元素对象
|
||||
const editorRef =ref<null | HTMLElement>(null)
|
||||
// 编译器
|
||||
const markedWorker = ref<null | Worker>(null)
|
||||
|
||||
|
||||
// 初始化Markdown编译线程
|
||||
function initMarked(){
|
||||
const worker = new Worker(new URL("./Markdown.worker.js", import.meta.url), {type: 'module' });
|
||||
worker.postMessage({
|
||||
type: 'init',
|
||||
})
|
||||
worker.onmessage = (e) => {
|
||||
switch (e.data.type) {
|
||||
case 'render': {
|
||||
// 加强安全,防止xss攻击
|
||||
console.log(e.data.content.editContent)
|
||||
// editorContent.value = DOMPurify.sanitize(e.data.content.editContent);
|
||||
viewerContent.value = DOMPurify.sanitize(e.data.content.viewContent);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
// worker.terminate();
|
||||
};
|
||||
markedWorker.value = worker
|
||||
}
|
||||
|
||||
let isUpdating = false;
|
||||
|
||||
const getCursorPos = (event) => {
|
||||
const content = event.target.innerText;
|
||||
const selection = window.getSelection();
|
||||
const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
|
||||
|
||||
if (range) {
|
||||
// 获取光标位置(相对于整个内容的偏移量)
|
||||
const cursorPosition = range.startOffset;
|
||||
|
||||
// 将内容按换行符分割成数组
|
||||
const lines = content.split(/\n/i);
|
||||
|
||||
// 累计每行的长度,找到光标所在的行
|
||||
let lineIndex = 0;
|
||||
let cumulativeLength = 0;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
cumulativeLength += lines[i].length;
|
||||
|
||||
// 如果光标位置小于当前累计长度,说明光标在这一行
|
||||
if (cursorPosition <= cumulativeLength) {
|
||||
lineIndex = i + 1; // 行号从1开始
|
||||
break;
|
||||
}
|
||||
|
||||
// 考虑换行符的长度(HTML 中的 `<br>`)
|
||||
cumulativeLength += 4; // `<br>` 的长度
|
||||
}
|
||||
console.log("光标所在的行:", lineIndex, range.startOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// 实时监听编辑器输入事件
|
||||
const handleEditorInput = async (event) => {
|
||||
getCursorPos(event)
|
||||
const editorContent = event.target.innerText;
|
||||
markedWorker.value?.postMessage({
|
||||
type: 'render',
|
||||
content: editorContent,
|
||||
})
|
||||
// restoreCaretPosition( event.target, caretPos);
|
||||
isUpdating = false;
|
||||
};
|
||||
|
||||
// 手动获取内容(通过按钮触发)
|
||||
const getEditorContent = () => {
|
||||
if (editorRef.value) {
|
||||
const text = editorRef.value.innerText.trim();
|
||||
console.log("手动获取:", text);
|
||||
}
|
||||
};
|
||||
// 处理粘贴内容(自动去除粘贴的 HTML 标签)
|
||||
const handleEditorPaste = (event) => {
|
||||
event.preventDefault(); // 阻止默认粘贴行为
|
||||
const text = event.clipboardData.getData("text/plain"); // 获取纯文本
|
||||
document.execCommand("insertText", false, text); // 插入纯文本
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initMarked()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="NiMarkdown">
|
||||
<header></header>
|
||||
<main>
|
||||
<div class="markdownEditor"
|
||||
ref="editorRef"
|
||||
contenteditable="true"
|
||||
@input="handleEditorInput"
|
||||
v-html="editorContent"
|
||||
/>
|
||||
<div class="markdownViewer"
|
||||
v-html="viewerContent"
|
||||
/>
|
||||
</main>
|
||||
<footer></footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.NiMarkdown{
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
& > header{
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
& > footer{
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
& > main{
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
& > div{
|
||||
position: relative;
|
||||
width: 50%;
|
||||
border: 1px solid var(--Ni-theme-border-color);
|
||||
overflowY: auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.markdownEditor{
|
||||
outline: none;
|
||||
}
|
||||
.markdownViewer{
|
||||
|
||||
/* 代码块样式 */
|
||||
:deep(pre) {
|
||||
white-space: pre-wrap;
|
||||
background: #cdcdcd;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
|
||||
code {
|
||||
font-family: 'Consolas', 'Fira Code', monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
/* 确保代码块正确换行 */
|
||||
code {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.code-line {
|
||||
display: flex;
|
||||
min-height: 1em; /* 防止空行高度塌陷 */
|
||||
}
|
||||
|
||||
.line-number {
|
||||
width: 40px;
|
||||
padding-right: 12px;
|
||||
color: #666;
|
||||
text-align: right;
|
||||
user-select: none;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.line-content {
|
||||
flex-grow: 1;
|
||||
white-space: pre-wrap; /* 允许代码换行 */
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
201
components/Ni/Markdown.worker.js
Normal file
201
components/Ni/Markdown.worker.js
Normal file
@ -0,0 +1,201 @@
|
||||
import {Marked} from 'marked';
|
||||
import hljs from 'highlight.js';
|
||||
import markedFootnote from 'marked-footnote'
|
||||
import markedCodeFormat from 'marked-code-format'
|
||||
import { markedEmoji } from 'marked-emoji';
|
||||
|
||||
postMessage({
|
||||
type: 'init',
|
||||
data: 'Hello World!'
|
||||
})
|
||||
|
||||
|
||||
// 默认编辑器配置
|
||||
const defaoltMarkOption = {
|
||||
async: true, // 同步编译
|
||||
pedantic: false,
|
||||
gfm: true,
|
||||
silent: true, // 输出错误
|
||||
breaks: false, // 将回车编程<br/>
|
||||
}
|
||||
|
||||
// 自定义行号注入函数
|
||||
const injectLineNumbers = (highlightedCode) => {
|
||||
const lines = highlightedCode.split('\n')
|
||||
|
||||
// 移除最后一行空行(常见于代码块末尾的换行)
|
||||
if (lines[lines.length - 1] === '') lines.pop()
|
||||
|
||||
// 为每行添加行号容器
|
||||
return lines.map((line, index) => `<div class="code-line"><span class="line-number">${index + 1}</span><span class="line-content">${line}</span></div>`).join('')
|
||||
}
|
||||
|
||||
// 编辑器render配置
|
||||
const editorRenderOption = {
|
||||
space: (...args) => {
|
||||
console.log(args, 'space')
|
||||
return `<div>space</div>`
|
||||
},
|
||||
code: (...args) => {
|
||||
console.log(args, 'code')
|
||||
return `<div>xscode</div>`
|
||||
},
|
||||
blockquote: (...args) => {
|
||||
console.log(args, 'blockquote')
|
||||
return `<div>blockquote</div>`
|
||||
},
|
||||
html: (...args) => {
|
||||
console.log(args, 'html')
|
||||
return `<div>html</div>`
|
||||
},
|
||||
heading: (...args) => {
|
||||
console.log(args[0], 'heading')
|
||||
return `<h${args[0].depth}>${args[0].raw}</h${args[0].depth}>`
|
||||
},
|
||||
hr: (...args) => {
|
||||
console.log(args, 'hr')
|
||||
return `<div>hr</div>`
|
||||
},
|
||||
list: (...args) => {
|
||||
console.log(args, 'list')
|
||||
return `<div>list</div>`
|
||||
},
|
||||
listitem: (...args) => {
|
||||
console.log(args, 'listitem')
|
||||
return `<div>listitem</div>`
|
||||
},
|
||||
checkbox: (...args) => {
|
||||
console.log(args, 'checkbox')
|
||||
return `<div>checkbox</div>`
|
||||
},
|
||||
paragraph: (...args) => {
|
||||
console.log(args, 'paragraph')
|
||||
return `<div>${args[0].raw}</div>`
|
||||
},
|
||||
table: (...args) => {
|
||||
console.log(args, 'table')
|
||||
return `<div>table</div>`
|
||||
},
|
||||
tablerow: (...args) => {
|
||||
console.log(args, 'tablerow')
|
||||
return `<div>tablerow</div>`
|
||||
},
|
||||
tablecell: (...args) => {
|
||||
console.log(args, 'tablecell')
|
||||
return `<div>tablecell</div>`
|
||||
},
|
||||
strong: (...args) => {
|
||||
console.log(args, 'strong')
|
||||
return `<div>strong</div>`
|
||||
},
|
||||
em: (...args) => {
|
||||
console.log(args, 'em')
|
||||
return `<div>em</div>`
|
||||
},
|
||||
codespan: (...args) => {
|
||||
console.log(args, 'codespan')
|
||||
return `<div>codespan</div>`
|
||||
},
|
||||
br: (...args) => {
|
||||
console.log(args, 'br')
|
||||
return `<div>br</div>`
|
||||
},
|
||||
del: (...args) => {
|
||||
console.log(args, 'del')
|
||||
return `<div>del</div>`
|
||||
},
|
||||
link: (...args) => {
|
||||
console.log(args, 'link')
|
||||
return `<div>link</div>`
|
||||
},
|
||||
image: (...args) => {
|
||||
console.log(args, 'image')
|
||||
return `<div>image</div>`
|
||||
},
|
||||
text: (...args) => {
|
||||
console.log(args, 'text')
|
||||
return `<div>text</div>`
|
||||
},
|
||||
}
|
||||
// 显示器render配置
|
||||
const viewerRenderOption = {
|
||||
code: ({text, lang}) => {
|
||||
// console.log(text);
|
||||
const validLang = hljs.getLanguage(lang) ? lang : 'plaintext'
|
||||
const highlighted = hljs.highlight(text, {language: lang}).value
|
||||
// console.log(highlighted)
|
||||
const withLineNumbers = injectLineNumbers(highlighted)
|
||||
return `<pre class="hljs ${validLang}"><code>${withLineNumbers}</code></pre>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 编辑器Marked编译器
|
||||
const editorMarked = new Marked({
|
||||
...defaoltMarkOption,
|
||||
renderer: editorRenderOption
|
||||
});
|
||||
// 显示器Marked编译器
|
||||
const viewerMarked = new Marked({
|
||||
...defaoltMarkOption,
|
||||
renderer: viewerRenderOption
|
||||
});
|
||||
|
||||
|
||||
// 编辑器插件
|
||||
editorMarked.use(markedCodeFormat()).use()
|
||||
|
||||
// 显示器插件
|
||||
// 自定义表情快速插入
|
||||
const markedEmojiOptions = {
|
||||
emojis: {
|
||||
"heart": "❤️",
|
||||
"tada": "🎉"
|
||||
},
|
||||
renderer: (token) => {
|
||||
console.log(token)
|
||||
return token.emoji
|
||||
}
|
||||
};
|
||||
// 支持脚注[^1] 会影响原始文本
|
||||
viewerMarked.use(markedFootnote()).use(markedCodeFormat()).use(markedEmoji(markedEmojiOptions))
|
||||
|
||||
addEventListener('message', async (e) => {
|
||||
console.log(e.data)
|
||||
switch (e.data.type) {
|
||||
case 'render': {
|
||||
// const editContent = await makeEditContent(e.data.content)
|
||||
const viewContent = await makeViewContent(e.data.content)
|
||||
postMessage({
|
||||
type: 'render',
|
||||
content: {
|
||||
viewContent,
|
||||
// editContent,
|
||||
},
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
// 函数
|
||||
|
||||
// 使用编译
|
||||
async function makeEditContent(contents){
|
||||
// 去除html innerText两端的空格,将 转化为空格
|
||||
let content = contents.trim().replace(/ /g, ' ');
|
||||
// 过滤不能识别,难识别的字符,空字符
|
||||
content = content.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,"");
|
||||
// 编译
|
||||
content = await editorMarked.parse(content)
|
||||
return content
|
||||
}
|
||||
async function makeViewContent(contents){
|
||||
// 过滤不能识别,难识别的字符,空字符
|
||||
let content = contents.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,"");
|
||||
// 编译
|
||||
content = await viewerMarked.parse(content)
|
||||
return content
|
||||
}
|
@ -13,6 +13,10 @@ export default defineNuxtConfig({
|
||||
esbuildOptions: {
|
||||
target: 'esnext'
|
||||
}
|
||||
},
|
||||
|
||||
worker: {
|
||||
format: 'es'
|
||||
}
|
||||
},
|
||||
compatibilityDate: '2024-11-01',
|
||||
|
76
package-lock.json
generated
76
package-lock.json
generated
@ -16,6 +16,9 @@
|
||||
"highlight.js": "^11.11.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"marked": "^15.0.11",
|
||||
"marked-code-format": "^1.1.6",
|
||||
"marked-emoji": "^2.0.0",
|
||||
"marked-footnote": "^1.2.4",
|
||||
"mysql2": "^3.14.0",
|
||||
"nuxt": "^3.16.2",
|
||||
"redis": "^4.7.0",
|
||||
@ -6157,6 +6160,15 @@
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/attributes-parser": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/attributes-parser/-/attributes-parser-2.2.3.tgz",
|
||||
"integrity": "sha512-zjOUWt95la8AdUO+kP1GBOonWrV5jy9NjJP+z9tva/DSA6FIzGKcN/gk3tdqQf/pOeB8dkyd3FCPrjhELMmrkg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"json-loose": "^1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.21",
|
||||
"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz",
|
||||
@ -7710,7 +7722,7 @@
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz",
|
||||
"resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.2.5.tgz",
|
||||
"integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
@ -10405,6 +10417,15 @@
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-loose": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/json-loose/-/json-loose-1.2.4.tgz",
|
||||
"integrity": "sha512-lwMWNC5pvVI33rhYWmAsmtICWE2IH7euDY/iIPeMFE5AuzAifYgqQrjqSMzwbrFV6MWPs41XD+CajElHI4cZMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"moo": "^0.5.2"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-to-typescript-lite": {
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/json-schema-to-typescript-lite/-/json-schema-to-typescript-lite-14.1.0.tgz",
|
||||
@ -11403,6 +11424,37 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/marked-code-format": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmmirror.com/marked-code-format/-/marked-code-format-1.1.6.tgz",
|
||||
"integrity": "sha512-DSzefm4DvOOGNdtCx+WekoLyXd8rwcEROSzyydnbJ3rQ2vuE2dKW8cpJui/ulUUHMvyrF9TCq351Fqjd81ocEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"attributes-parser": "^2.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"marked": ">=7.0.0",
|
||||
"prettier": ">=3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/marked-emoji": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/marked-emoji/-/marked-emoji-2.0.0.tgz",
|
||||
"integrity": "sha512-oTZ8fqbdVDHFQnqCE1tg4ND7zEd7cUVNHliR9Ldu4eys0J86uz/5Uksjd2mt5xcX16OOScDEr3MmPjajI/ZDHA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"marked": ">=4 <16"
|
||||
}
|
||||
},
|
||||
"node_modules/marked-footnote": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/marked-footnote/-/marked-footnote-1.2.4.tgz",
|
||||
"integrity": "sha512-DB2Kl+wFh6YwZd70qABMY6WUkG1UuyqoNTFoDfGyG79Pz24neYtLBkB+45a7o72V7gkfvbC3CGzIYFobxfMT1Q==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"marked": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@ -11670,6 +11722,12 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/moo": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmmirror.com/moo/-/moo-0.5.2.tgz",
|
||||
"integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz",
|
||||
@ -13539,6 +13597,22 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.5.3.tgz",
|
||||
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/pretty-bytes": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
|
||||
|
@ -23,6 +23,9 @@
|
||||
"highlight.js": "^11.11.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"marked": "^15.0.11",
|
||||
"marked-code-format": "^1.1.6",
|
||||
"marked-emoji": "^2.0.0",
|
||||
"marked-footnote": "^1.2.4",
|
||||
"mysql2": "^3.14.0",
|
||||
"nuxt": "^3.16.2",
|
||||
"redis": "^4.7.0",
|
||||
|
@ -66,7 +66,6 @@ const formatBlogMenuListToPidMap = (blogMenuList: Array<ShowBlogMenu>) => {
|
||||
const id = menuItem.id;
|
||||
if(pidObj[id]){
|
||||
menuItem.childrenLength = pidObj[id].length
|
||||
consola.info(menuItem.name, menuItem.childrenLength)
|
||||
}
|
||||
idMap.set(id, menuItem)
|
||||
}
|
||||
@ -271,6 +270,7 @@ onMounted(() => {
|
||||
overflow: auto;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
& > header{
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
|
@ -1,10 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
const blogContent = ref('');
|
||||
const blogId = ref<string>(route.params.blogId.toString());
|
||||
|
||||
watch(() => route, () => {
|
||||
blogId.value = route.params.blogId.toString();
|
||||
})
|
||||
onMounted(() => {
|
||||
consola.info('BlogId', blogId.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="BlogEntity">
|
||||
<HomeBlogMarked></HomeBlogMarked>
|
||||
<NiMarkdown :content="blogContent" :contentId="blogId"></NiMarkdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -12,5 +21,6 @@
|
||||
.BlogEntity{
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
23
测试输入.html
Normal file
23
测试输入.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<style>
|
||||
html,body{
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.box{
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="box"
|
||||
contenteditable="true"></div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user