All checks were successful
deploy to server / build-and-deploy (push) Successful in 6m51s
- 后端CMS字段由Markdown改为WYSIWYG因此前端做出对应修改
81 lines
2.0 KiB
TypeScript
81 lines
2.0 KiB
TypeScript
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;
|
||
};
|