feat!: 将QuestionList的内容渲染由Markdown改为HTML
All checks were successful
deploy to server / build-and-deploy (push) Successful in 6m51s

- 后端CMS字段由Markdown改为WYSIWYG因此前端做出对应修改
This commit is contained in:
2025-11-14 00:00:00 +08:00
parent 154943815d
commit c860621e7a
9 changed files with 322 additions and 1 deletions

View File

@ -0,0 +1,80 @@
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;
};