From 382bdf6579ab20e7203186bc65e3eeb103682977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?wifi=E6=AD=AAf?= <1402772884@qq.com> Date: Wed, 29 Jan 2025 13:22:06 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(mini-markdown-editor):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=BD=93=E5=86=85=E5=AE=B9=E4=BF=AE=E6=94=B9=E5=90=8E?= =?UTF-8?q?,=20=E7=9B=AE=E5=BD=95=E6=A0=87=E9=A2=98=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E6=BB=9A=E5=8A=A8=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Sidebar/Contents.tsx | 59 ++++++++++++++++--- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/packages/mini-markdown-editor/src/components/Sidebar/Contents.tsx b/packages/mini-markdown-editor/src/components/Sidebar/Contents.tsx index cc68ed1..5319fc6 100644 --- a/packages/mini-markdown-editor/src/components/Sidebar/Contents.tsx +++ b/packages/mini-markdown-editor/src/components/Sidebar/Contents.tsx @@ -6,6 +6,7 @@ import { useEditorContentStore } from "@/store/editor"; const Contents: FC = () => { const previewView = useEditorContentStore((state) => state.previewView); const [titles, setTitles] = useState([]); + const [activeLink, setActiveLink] = useState(""); const preview = document.querySelector(".markdown-editor-preview") as HTMLElement | null; const getRootElement = () => { return preview?.querySelectorAll("h1, h2, h3, h4, h5, h6") as NodeListOf; @@ -29,7 +30,11 @@ const Contents: FC = () => { // 初始化时立即执行一次 if (rootElement.length > 0) { - setTitles(addAnchor()); + const initialTitles = addAnchor(); + setTitles(initialTitles); + if (initialTitles.length > 0) { + setActiveLink(initialTitles[0].href); + } } const observer = new MutationObserver(() => { @@ -37,7 +42,11 @@ const Contents: FC = () => { const elements = getRootElement(); if (elements && elements.length > 0) { requestAnimationFrame(() => { - setTitles(formatContents(elements)); + const newTitles = formatContents(elements); + setTitles(newTitles); + if (newTitles.length > 0 && !activeLink) { + setActiveLink(newTitles[0].href); + } }); } }); @@ -54,11 +63,42 @@ const Contents: FC = () => { }; }, [preview, rootElement]); - // 自定义高亮锚点(默认选中第一个) - const getCurrentAnchor = (activeLink: string): string => { - if (!activeLink && titles.length > 0) { - activeLink = titles[0].href; - } + // 监听滚动更新高亮 + useEffect(() => { + if (!preview) return; + + const handleScroll = () => { + const elements = getRootElement(); + if (!elements) return; + + // 找到当前视口中最靠近顶部的标题 + let closestTitle = null; + let minDistance = Infinity; + + elements.forEach((element) => { + // 判断哪个标题离视口顶部最近 + const rect = element.getBoundingClientRect(); + const distance = Math.abs(rect.top); + if (distance < minDistance) { + minDistance = distance; + closestTitle = element; + } + }); + + if (closestTitle) { + const line = (closestTitle as HTMLElement).getAttribute("data-line"); + if (line) { + setActiveLink(`#${line}`); + } + } + }; + + preview.addEventListener("scroll", handleScroll); + return () => preview.removeEventListener("scroll", handleScroll); + }, [preview]); + + // 自定义高亮锚点 + const getCurrentAnchor = () => { return activeLink; }; @@ -71,8 +111,11 @@ const Contents: FC = () => { ) => { e.preventDefault(); if (link.href && previewView) { - const targetElement = previewView.querySelector(`[data-line="${link.href}"]`); + // 从href中提取data-line值 + const dataLine = link.href.replace("#", ""); + const targetElement = previewView.querySelector(`[data-line="${dataLine}"]`); if (targetElement) { + setActiveLink(link.href); targetElement.scrollIntoView({ behavior: "smooth", block: "center", -- Gitee From b384199738c8fb09b26e4e10d712c7be0e3874eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?wifi=E6=AD=AAf?= <1402772884@qq.com> Date: Fri, 31 Jan 2025 04:22:54 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(mini-markdown-editor):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=AF=BC=E5=87=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/assets/images/output-pdf.svg | 1 - .../src/assets/images/output.svg | 1 + .../src/assets/styles/preview.css | 1 - .../src/components/Sidebar/Output.tsx | 73 +++++++++++++++++++ .../src/components/Toolbar/ShowLayout.tsx | 18 +++++ .../src/config/toolbar/base.tsx | 10 +-- .../mini-markdown-editor/src/types/toolbar.ts | 2 +- .../src/utils/output-html.ts | 54 ++++++++++++++ .../src/utils/output-pdf.ts | 3 + 9 files changed, 154 insertions(+), 9 deletions(-) delete mode 100644 packages/mini-markdown-editor/src/assets/images/output-pdf.svg create mode 100644 packages/mini-markdown-editor/src/assets/images/output.svg create mode 100644 packages/mini-markdown-editor/src/components/Sidebar/Output.tsx create mode 100644 packages/mini-markdown-editor/src/utils/output-html.ts create mode 100644 packages/mini-markdown-editor/src/utils/output-pdf.ts diff --git a/packages/mini-markdown-editor/src/assets/images/output-pdf.svg b/packages/mini-markdown-editor/src/assets/images/output-pdf.svg deleted file mode 100644 index 04e056a..0000000 --- a/packages/mini-markdown-editor/src/assets/images/output-pdf.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/mini-markdown-editor/src/assets/images/output.svg b/packages/mini-markdown-editor/src/assets/images/output.svg new file mode 100644 index 0000000..5583c43 --- /dev/null +++ b/packages/mini-markdown-editor/src/assets/images/output.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/mini-markdown-editor/src/assets/styles/preview.css b/packages/mini-markdown-editor/src/assets/styles/preview.css index 6080978..dd022b9 100644 --- a/packages/mini-markdown-editor/src/assets/styles/preview.css +++ b/packages/mini-markdown-editor/src/assets/styles/preview.css @@ -56,7 +56,6 @@ } .mini-md-image { - width: 100%; max-width: 100%; border: 1px solid #e6e6e6; border-radius: 3px; diff --git a/packages/mini-markdown-editor/src/components/Sidebar/Output.tsx b/packages/mini-markdown-editor/src/components/Sidebar/Output.tsx new file mode 100644 index 0000000..faa9306 --- /dev/null +++ b/packages/mini-markdown-editor/src/components/Sidebar/Output.tsx @@ -0,0 +1,73 @@ +import styled from "styled-components"; +import { Button, Form, Input, Select } from "antd"; +import { exportHTML } from "@/utils/output-html"; +import { useEditorContentStore } from "@/store/editor"; +import { useState } from "react"; +import { exportPdf } from "@/utils/output-pdf"; + +const Wrapper = styled.div` + width: 100%; + height: 100%; + padding: 10px; + color: #3f4a54; +`; + +type FieldType = { + "file-type": "PDF" | "HTML"; + "file-name": string; +}; + +const Output = () => { + const [form] = Form.useForm(); + const preview = useEditorContentStore((state) => state.previewView); + const [loading, setLoading] = useState(false); + + const handleExport = async () => { + const { "file-type": fileType, "file-name": fileName } = form.getFieldsValue(); + setLoading(true); + switch (fileType) { + case "PDF": { + await exportPdf(preview!, fileName); + break; + } + case "HTML": { + if (!preview) return; + await exportHTML(preview, fileName); + break; + } + default: + break; + } + setLoading(false); + }; + + return ( + +
+ label="导出文件类型" name="file-type"> + + + + + + +
+ ); +}; + +export default Output; diff --git a/packages/mini-markdown-editor/src/components/Toolbar/ShowLayout.tsx b/packages/mini-markdown-editor/src/components/Toolbar/ShowLayout.tsx index de7727d..736c555 100644 --- a/packages/mini-markdown-editor/src/components/Toolbar/ShowLayout.tsx +++ b/packages/mini-markdown-editor/src/components/Toolbar/ShowLayout.tsx @@ -5,9 +5,11 @@ import WriteIcon from "@/assets/images/write.svg?raw"; import PreviewIcon from "@/assets/images/perview.svg?raw"; import ContentsIcon from "@/assets/images/contents.svg?raw"; import HelpIcon from "@/assets/images/help.svg?raw"; +import OutputIcon from "@/assets/images/output.svg?raw"; import { useToolbarStore } from "@/store/toolbar"; import SidebarContents from "@/components/Sidebar/Contents"; import SidebarHelp from "@/components/Sidebar/Help"; +import SidebarOutput from "@/components/Sidebar/Output"; const Wrapper = styled.div<{ $isSelect: boolean }>` display: flex; @@ -78,3 +80,19 @@ export const Help: FC = () => { ); }; + +// 导出按钮 +export const Output: FC = () => { + const ComponentsMark = "Output"; + const { isSidebar, componentMark, setSidebar } = useToolbarStore(); + return ( + <> + setSidebar(, ComponentsMark)}> + + + + ); +}; diff --git a/packages/mini-markdown-editor/src/config/toolbar/base.tsx b/packages/mini-markdown-editor/src/config/toolbar/base.tsx index 8ee235b..5ff8d98 100644 --- a/packages/mini-markdown-editor/src/config/toolbar/base.tsx +++ b/packages/mini-markdown-editor/src/config/toolbar/base.tsx @@ -14,13 +14,12 @@ import ImageIcon from "@/assets/images/image.svg"; import TableIcon from "@/assets/images/table.svg"; import Undo from "@/assets/images/undo.svg"; import Redo from "@/assets/images/redo.svg"; -import OutputPDFIcon from "@/assets/images/output-pdf.svg"; import { InsertTextEvent } from "./event"; import { ToolbarItem } from "@/types/toolbar"; // 组件 import Upload from "@/components/Toolbar/Upload"; import FullScreen from "@/components/Toolbar/FullScreen"; -import { Contents, Read, Write, Help } from "@/components/Toolbar/ShowLayout"; +import { Contents, Read, Write, Help, Output } from "@/components/Toolbar/ShowLayout"; export const toolbar: ToolbarItem[] = [ { @@ -182,13 +181,12 @@ export const toolbar: ToolbarItem[] = [ component: , }, { - type: "pdf", - icon: OutputPDFIcon, - title: "导出为PDF", + type: "output", + component: , }, ]; -// 渲染其他定制节点 +// 渲染列表其他定制节点 export const render = { "image-upload": , }; diff --git a/packages/mini-markdown-editor/src/types/toolbar.ts b/packages/mini-markdown-editor/src/types/toolbar.ts index 75647c5..5295325 100644 --- a/packages/mini-markdown-editor/src/types/toolbar.ts +++ b/packages/mini-markdown-editor/src/types/toolbar.ts @@ -53,4 +53,4 @@ export type ToolbarType = | "preview" | "contents" | "help" - | "pdf"; + | "output"; diff --git a/packages/mini-markdown-editor/src/utils/output-html.ts b/packages/mini-markdown-editor/src/utils/output-html.ts new file mode 100644 index 0000000..9867b30 --- /dev/null +++ b/packages/mini-markdown-editor/src/utils/output-html.ts @@ -0,0 +1,54 @@ +export const exportHTML = (element: HTMLElement, fileName: string) => { + return new Promise((resolve) => { + if (!element) { + console.error("Element not found"); + return; + } + + // 获取元素的HTML内容 + const htmlContent = element.outerHTML; + + // 获取所有样式 + const styles = Array.from(document.styleSheets) + .map((sheet) => { + try { + return Array.from(sheet.cssRules) + .map((rule) => rule.cssText) + .join("\n"); + } catch (e) { + console.error("Error accessing stylesheet:", e); + return ""; + } + }) + .join("\n"); + + // 创建包含样式的HTML内容 + const fullHtmlContent = ` + + + + + + Exported HTML + + + + ${htmlContent} + + + `; + + const blob = new Blob([fullHtmlContent], { type: "text/html" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `${fileName}.html`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + resolve({}); + }); +}; diff --git a/packages/mini-markdown-editor/src/utils/output-pdf.ts b/packages/mini-markdown-editor/src/utils/output-pdf.ts new file mode 100644 index 0000000..6c5c09a --- /dev/null +++ b/packages/mini-markdown-editor/src/utils/output-pdf.ts @@ -0,0 +1,3 @@ +export const exportPdf = async (element: HTMLElement, filename: string) => { + console.log(element, filename); +}; -- Gitee