Files
jinshen-website/app/composables/useHtmlRenderer.ts
R2m1liA c860621e7a
All checks were successful
deploy to server / build-and-deploy (push) Successful in 6m51s
feat!: 将QuestionList的内容渲染由Markdown改为HTML
- 后端CMS字段由Markdown改为WYSIWYG因此前端做出对应修改
2025-11-14 00:00:05 +08:00

81 lines
2.0 KiB
TypeScript
Raw Permalink 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.

import { h, type VNode } from 'vue';
import { parseDocument } from 'htmlparser2';
import type { Element, Text, Node } from 'domhandler';
/** 标签映射的类型key=标签名value=渲染函数 */
export type HtmlRenderMap = Record<
string,
(node: Element, children: VNode[]) => VNode
>;
export interface HtmlRenderOptions {
/** 自定义标签渲染映射 */
map?: HtmlRenderMap;
/** 默认是否将未知标签渲染为原生标签(默认 true */
allowUnknownTags?: boolean;
}
export const useHtmlRenderer = (
html: string,
options: HtmlRenderOptions = {}
) => {
const { map = {}, allowUnknownTags = true } = options;
const doc = parseDocument(html);
function render(node: Node): VNode | string | null {
// 文本节点
if (node.type === 'text') {
const textNode = node as Text;
const content = textNode.data;
// ❗忽略"纯空白" 文本(换行、空格)
if (!content || /^\s+$/.test(content)) {
return null;
}
return content;
}
// 标签节点
if (node.type === 'tag') {
const elem = node as Element;
const rawName = elem.name ?? '';
const name = rawName.trim().toLowerCase();
// 标签名有效性校验
const isValidTag = /^[a-zA-Z][a-zA-Z0-9-]*$/.test(name);
if (!isValidTag) {
logger.warn(`Invalid tag name ignored: "${rawName}"`);
return null;
}
const children: VNode[] = (elem.children || [])
.map((child) => render(child))
.filter(Boolean) as VNode[];
// 自定义映射
if (map[name]) {
return map[name](elem, children);
}
// 默认将未知标签渲染为原生标签
if (allowUnknownTags) {
return h(name, elem.attribs || {}, children);
}
// 忽略无法渲染节点
return null;
}
return null;
}
const nodes = doc.children
.map((child) => render(child))
.filter(Boolean) as VNode[];
return nodes;
};