1. 项目背景与核心需求
在合同审查系统的开发过程中,我们面临一个关键挑战:如何将后端返回的Markdown格式合同文档转换为可视化的导航目录,并实现点击目录快速定位到文档对应位置的功能。这个需求看似简单,但实际开发中会遇到几个技术难点:
- 数据结构转换:Markdown是线性文本格式,而导航目录需要树形结构
- 层级关系处理:合同文档可能包含多级标题(#、##、###等),需要准确建立父子关系
- 精确定位:点击目录项时,需要平滑滚动到文档对应位置并高亮显示
经过技术选型,我们决定采用React+Ant Design的技术栈实现这个功能。Ant Design的Tree组件提供了良好的树形结构展示能力,而React的状态管理可以很好地处理数据流转和UI更新。
提示:在实际项目中,合同文档通常由法务人员使用Markdown编写,包含大量条款和子条款,良好的导航功能可以显著提升审查效率。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构设计
2.1 整体架构
系统采用分层架构设计,各层职责明确:
code复制数据层 → 解析层 → 展示层 → 交互层
- 数据层:接收后端返回的Markdown原始数据
- 解析层:将Markdown转换为树形结构
- 展示层:使用Ant Design Tree组件渲染目录树
- 交互层:处理用户点击事件,实现文档定位
2.2 关键技术选型
-
Markdown解析:
- 不依赖第三方库,直接使用正则表达式处理
- 优点:轻量、可控,避免引入不必要的依赖
- 适用场景:只需要解析标题结构的简单需求
-
树形结构构建:
- 使用栈(Stack)数据结构维护层级关系
- 时间复杂度O(n),适合大多数文档规模
-
UI组件:
- Ant Design Tree组件
- 内置展开/收起、选中状态管理等功能
- 支持自定义节点渲染
3. 核心实现细节
3.1 Markdown到树形结构的转换
3.1.1 数据预处理
首先需要处理后端返回的数据,统一转换为字符串格式:
javascript复制useEffect(() => {
if (!resultData || Object.keys(resultData).length === 0) return;
let markdownText = '';
if (typeof resultData === 'string') {
markdownText = resultData;
} else if (typeof resultData === 'object') {
// 分页文档按页码顺序拼接
Object.keys(resultData).sort().forEach(pageKey => {
markdownText += resultData[pageKey] + '\n';
});
}
// 继续解析...
}, [resultData]);
注意:实际项目中,后端可能返回分页的合同数据,需要按页码顺序正确拼接。
3.1.2 栈结构解析算法
核心解析算法使用栈来维护标题的层级关系:
javascript复制const lines = markdownText.trim().split('\n');
const structure = [];
const stack = []; // 用于跟踪各级父节点
lines.forEach((line, index) => {
const match = line.match(/^(#+)\s(.+)/);
if (match) {
const level = match[1].length; // 标题级别
const title = match[2]; // 标题文本
const key = `node-${index}`; // 唯一标识
const node = {
title,
key,
level,
children: []
};
if (level === 1) {
// 一级标题作为根节点
structure.push(node);
stack[0] = node;
stack.splice(1); // 清除更深层级的引用
} else {
// 多级标题处理
const parentLevel = level - 2;
const parent = stack[parentLevel];
if (parent) {
parent.children.push(node)
