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