website-01/components/front/ContentView.tsx
2026-06-22 14:43:46 +08:00

83 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import ReactMarkdown, { type Components } from 'react-markdown';
import { isHTMLContent } from '@/lib/content';
interface ContentViewProps {
content: string | null | undefined;
className?: string;
/**
* 显式指定内容格式,优先于内容探测。
* - 'html'富文本dangerouslySetInnerHTML 渲染
* - 'markdown':用 react-markdown 解析
* 不传时回退到 isHTMLContent 自动判断(旧调用方兼容)。
*/
format?: 'html' | 'markdown';
}
const mdComponents: Components = {
pre: ({ children }) => (
<pre className="my-4 overflow-x-auto rounded-md bg-gray-900 p-4 text-sm text-gray-100">
{children}
</pre>
),
code: ({ className: cls, children }) => {
// 行内 code无 language- 前缀)→ 灰底圆角;块级 code 由 <pre> 包裹
const isBlock = typeof cls === 'string' && cls.includes('language-');
if (isBlock) {
return <code className={cls}>{children}</code>;
}
return (
<code className="rounded bg-gray-100 px-1.5 py-0.5 text-sm text-pink-600">
{children}
</code>
);
},
table: ({ children }) => (
<table className="my-4 w-full border-collapse border border-gray-300 text-sm">
{children}
</table>
),
th: ({ children }) => (
<th className="border border-gray-300 bg-gray-50 px-3 py-2 text-left font-medium">
{children}
</th>
),
td: ({ children }) => (
<td className="border border-gray-300 px-3 py-2">{children}</td>
),
hr: () => <hr className="my-6 border-gray-200" />,
};
/**
* 智能内容渲染器:
* - HTML 内容(来自 RichEditor → 用 dangerouslySetInnerHTML 直接渲染
* - Markdown 内容(来自 .md 上传)→ 用 react-markdown 解析为 React 元素后渲染
* 两者都套 `.prose-rich` 类以复用全局排版样式。
*
* 注MD 内容用 suppressHydrationWarning 包裹,避免 react-markdown
* SSR/客户端解析在边界字符HTML 标签、空白)上产生 hydration 不一致。
*/
export function ContentView({ content, className, format }: ContentViewProps) {
if (!content?.trim()) return null;
const wrapperClass = className ? `prose-rich ${className}` : 'prose-rich';
const isHtml =
format === 'html' ? true : format === 'markdown' ? false : isHTMLContent(content);
if (isHtml) {
return (
<div
className={wrapperClass}
dangerouslySetInnerHTML={{ __html: content }}
/>
);
}
return (
<div className={wrapperClass} suppressHydrationWarning>
<ReactMarkdown components={mdComponents}>{content}</ReactMarkdown>
</div>
);
}