From c26a0c5e20dfdcc45b43315b50296e90e30995d8 Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期三, 07 五月 2025 11:41:53 +0800 Subject: [PATCH] 表格展示 --- src/components/chat/libs/markdown.ts | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 158 insertions(+), 10 deletions(-) diff --git a/src/components/chat/libs/markdown.ts b/src/components/chat/libs/markdown.ts index 0a002fe..1f9b8f6 100644 --- a/src/components/chat/libs/markdown.ts +++ b/src/components/chat/libs/markdown.ts @@ -1,21 +1,169 @@ -import highlight from 'highlight.js'; +// import highlight from 'highlight.js'; +import hljs from 'highlight.js'; import Markdown from 'markdown-it'; +// 瀵煎叆 highlight.js 鐨勬牱寮忥紝杩欓噷浣跨敤 github 涓婚 +import 'highlight.js/styles/github.css'; -const mdOptions: Markdown.Options = { +const md = new Markdown({ linkify: true, typographer: true, breaks: true, langPrefix: 'language-', - // 浠g爜楂樹寒 - highlight(str, lang) { - if (lang && highlight.getLanguage(lang)) { + highlight: (str: string, lang: string) => { + if (lang && hljs.getLanguage(lang)) { try { - return '<pre class="hljs"><code>' + highlight.highlight(lang, str, true).value + '</code></pre>'; - } catch (__) { - } + return hljs.highlight(str, { language: lang }).value; + } catch { + return str; + } } - return ''; + return str; }, +}); + +// 鑷畾涔変唬鐮佸潡娓叉煋 +md.renderer.rules.fence = (tokens, idx, options, env, slf) => { + const token = tokens[idx]; + const lang = token.info || ''; + const highlighted = options.highlight?.(token.content, lang, '') || token.content; + return ` + <div class="code-markdown-it-block"> + <div class="code-markdown-it-header"> + <div class="code-markdown-it-header-left"> + <span class="code-markdown-it-lang">${lang}</span> + </div> + <div class="code-markdown-it-header-right"> + <button title="澶嶅埗" class="code-markdown-it-copy" onclick="this.classList.add('copied'); setTimeout(() => this.classList.remove('copied'), 1500); navigator.clipboard.writeText(this.parentElement.parentElement.nextElementSibling.textContent)"> + <svg class="copy-icon" viewBox="0 0 16 16" width="16" height="16"> + <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path> + <path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path> + </svg> + <svg class="check-icon" viewBox="0 0 16 16" width="16" height="16"> + <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0 0 1 1.06-1.06L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path> + </svg> + </button> + <button title="灞曞紑" class="code-markdown-it-toggle" onclick="const block = this.parentElement.parentElement.parentElement; block.classList.toggle('collapsed'); this.title = block.classList.contains('collapsed') ? '灞曞紑' : '鎶樺彔'"> + <svg class="expand-icon" viewBox="0 0 24 24" width="16" height="16"> + <path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"></path> + </svg> + </button> + </div> + </div> + <pre class="code-markdown-it-content"><code class="${options.langPrefix}${lang}">${highlighted}</code></pre> + </div> + `.trim(); }; -export const md = new Markdown(mdOptions); +// 娣诲姞鑷畾涔夋牱寮� +const style = document.createElement('style'); +// 鑷畾涔夋钀芥覆鏌撹鍒� +md.renderer.rules.paragraph_open = (tokens, idx) => { + return '<p class="inline-block">'; // 娣诲姞 inline-block 绫绘潵鎺у埗鏄剧ず +}; + +// 鑷畾涔夎〃鏍兼覆鏌撹鍒� +md.renderer.rules.table_open = (tokens, idx) => { + return '<table class="markdown-table">'; +}; + +style.textContent = ` + .code-markdown-it-block { + margin: 1em 0; + border-radius: 6px; + overflow: hidden; + border: 1px solid #e1e4e8; + } + .code-markdown-it-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 16px; + background: #f6f8fa; + border-bottom: 1px solid #e1e4e8; + } + .code-markdown-it-header-left { + display: flex; + align-items: center; + } + .code-markdown-it-header-right { + display: flex; + align-items: center; + gap: 8px; + } + .code-markdown-it-lang { + font-size: 14px; + font-weight: 600; + color: #24292e; + // text-transform: uppercase; + } + .code-markdown-it-copy { + display: inline-flex; + align-items: center; + justify-content: center; + background: none; + border: none; + padding: 4px; + cursor: pointer; + color: #586069; + border-radius: 4px; + } + .code-markdown-it-copy:hover { + background: #f3f4f6; + color: #24292e; + } + .code-markdown-it-copy .check-icon { + display: none; + } + .code-markdown-it-copy.copied .copy-icon { + display: none; + } + .code-markdown-it-copy.copied .check-icon { + display: block; + color: #22863a; + } + .code-markdown-it-toggle { + background: none; + border: none; + padding: 4px; + cursor: pointer; + color: #586069; + display: flex; + align-items: center; + } + .code-markdown-it-toggle:hover { + color: #24292e; + } + .code-markdown-it-content { + margin: 0; + padding: 16px; + overflow-x: auto; + } + .code-markdown-it-block.collapsed .code-markdown-it-content { + display: none; + } + .code-markdown-it-block.collapsed .expand-icon { + transform: rotate(-90deg); + } + .expand-icon { + transition: transform 0.2s; + fill: currentColor; + } + .copy-icon, .check-icon { + fill: currentColor; + } + .markdown-table { + border-collapse: collapse; + width: 100%; + } + .markdown-table thead { + background-color: #f2f2f2; + } + .markdown-table th, + .markdown-table td { + border: 1px solid #ebebeb; + padding: 8px; + text-align: center; + } +`; +document.head.appendChild(style); +export { md }; -- Gitblit v1.9.3