根据draft的editorState生成结构化数据用来生成目录

277 阅读3分钟

初始化editorState数据

const initialState = {
  blocks: [
    {
      key: _.uniqueId('key'),
      text: 'H2标题-0',
      type: 'header-two',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'H3标题-0-1',
      type: 'header-three',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'H1标题',
      type: 'header-one',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'H3标题-1-1-1',
      type: 'header-three',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'H2标题-1-1',
      type: 'header-two',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: '文字段落:',
      type: 'unstyled',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'blockquote',
      type: 'blockquote',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'code-block',
      type: 'code',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'figure',
      type: 'atomic',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'item0',
      type: 'unordered-list-item',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'item1',
      type: 'unordered-list-item',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'H2标题-1-2',
      type: 'header-two',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'H3标题-1-2-1',
      type: 'header-three',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'H3标题-1-2-2',
      type: 'header-three',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'H2标题-1-3',
      type: 'header-two',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'H3标题-1-3-1',
      type: 'header-three',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'H1标题-2',
      type: 'header-one',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
    {
      key: _.uniqueId('key'),
      text: 'H3标题-2-1-2',
      type: 'header-three',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {},
    },
  ],
  entityMap: {},
};

将数据通过页面交互等方式传递给getToc方法

export const getToc = (editorState: EditorState): TocItem[] => {
  const editorStateObj = editorState.getCurrentContent().toJS();
  const { blockMap } = editorStateObj;
  const blockValues = _.values(blockMap);
  // 只保留标题类型
  const dataSource: BlockMapValuesItem[] = [];
  _.forEach(blockValues, (bv) => {
    if (_.includes(typeArr, bv.type)) {
      dataSource.push({ ...bv, level: typeMap[`${bv.type}`] });
    }
  });
  if (_.isEmpty(dataSource)) {
    return [];
  }
  const headerItem: BlockMapValuesItem = _.head(dataSource) || initBlockItem;
  const targetToc: TocItem[] = [
    {
      label: headerItem.text,
      type: headerItem.type,
      level: headerItem.level,
      children: [],
    },
  ];
  _.forEach(dataSource, (ds, dsi) => {
    if (dsi !== 0) {
      recursiveTitle(ds, targetToc);
    }
  });
  console.log(JSON.stringify(targetToc));

  return targetToc;
};

getToc拿到editorState变量后,根据其getCurrentContent方法,通过对blockMap处理后获取dataSource数据

通过recursiveTitle函数递归得到最终结构化数据

  1. 将当前标题数据dataSource的第一个项先放入targetToc中
  2. 从索引1开始遍历dataSource,将每一项curItem的level值跟targetToc的最后一项lastTargetItem的level值进行对比
  3. 如果curItem的level大于lastTargetItem的level,那么说明curItem的标题层级要比lastTargetItem深,此时对lastTargetItem的children进行判断,执行步骤4;否则执行步骤5
  4. 如果lastTargetItem的children为空,直接将curItem放入该children;如果children不为空,那么将children作为targetToc进行递归操作
  5. curItem的level小于或者等于lastTargetItem的level时,说明当前层级比lastTargetItem的层级要浅,那么层级之间就不是嵌套而是并列关系,则直接放入targetToc
/**
 * 将当前节点放入所属父节点的children
 * @param curItem 当前待插入的标题信息
 * @param targetToc 参照层级
 */
function recursiveTitle(curItem: BlockMapValuesItem, targetToc: TocItem[]) {
  const lastTargetItem: TocItem = _.last(targetToc) || initTocItem;
  if (curItem.level > lastTargetItem.level) {
    // 当前层级比参照层级深
    if (_.isEmpty(lastTargetItem.children)) {
      // 参照层级的children为空时
      lastTargetItem.children.push({
        label: curItem.text,
        type: curItem.type,
        level: curItem.level,
        children: [],
      });
    } else {
      // 参照层级的children不为空时,继续传递参照层级的children作为参照
      recursiveTitle(curItem, lastTargetItem.children);
    }
  } else {
    // 当前层级等于或者小于参照层级,
    targetToc.push({
      label: curItem.text,
      type: curItem.type,
      level: curItem.level,
      children: [],
    });
  }
}

最终得到数据的结构样例:

[
    {
        "label": "H2标题-0",
        "type": "header-two",
        "level": 2,
        "children": [
            {
                "label": "H3标题-0-1",
                "type": "header-three",
                "level": 3,
                "children": []
            }
        ]
    },
    {
        "label": "H1标题",
        "type": "header-one",
        "level": 1,
        "children": [
            {
                "label": "H3标题-1-1-1",
                "type": "header-three",
                "level": 3,
                "children": []
            },
            {
                "label": "H2标题-1-1",
                "type": "header-two",
                "level": 2,
                "children": []
            },
            {
                "label": "H2标题-1-2",
                "type": "header-two",
                "level": 2,
                "children": [
                    {
                        "label": "H3标题-1-2-1",
                        "type": "header-three",
                        "level": 3,
                        "children": []
                    },
                    {
                        "label": "H3标题-1-2-2",
                        "type": "header-three",
                        "level": 3,
                        "children": []
                    }
                ]
            },
            {
                "label": "H2标题-1-3",
                "type": "header-two",
                "level": 2,
                "children": [
                    {
                        "label": "H3标题-1-3-1",
                        "type": "header-three",
                        "level": 3,
                        "children": []
                    }
                ]
            }
        ]
    },
    {
        "label": "H1标题-2",
        "type": "header-one",
        "level": 1,
        "children": [
            {
                "label": "H3标题-2-1-2",
                "type": "header-three",
                "level": 3,
                "children": []
            }
        ]
    }
]

欢迎扫码关注作者公众号