feat!: 将QuestionList的内容渲染由Markdown改为HTML
All checks were successful
deploy to server / build-and-deploy (push) Successful in 6m51s
All checks were successful
deploy to server / build-and-deploy (push) Successful in 6m51s
- 后端CMS字段由Markdown改为WYSIWYG因此前端做出对应修改
This commit is contained in:
80
app/composables/useHtmlRenderer.ts
Normal file
80
app/composables/useHtmlRenderer.ts
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user