基于mkdocs-material实现的帮助中心(markdown + 中文搜索 + 图片放大)

沐乃

需求

  • 整站文档使用markdown,方便产品&运营童鞋们编写
  • 支持搜索
  • 点击图片能放大

最终完成效果如下:
图片描述
左侧导航栏支持多层级嵌套,右侧为当前文档内标题导航。

顶部右侧搜索栏(目前只支持分词搜索,不支持整句):
搜索区域

图片放大:
图片描述

实现

mkdocs-demo.git

更全面的信息请移步官网:
mkdocs官网
mkdocs-material官网

环境搭建

python: 3.7.2

# 安装依赖
pip install mkdocs mkdocs-material
pip install jieba
# 本地调试
mkdocs serve
# 打包
mkdocs build
包名 模块名 版本
mkdocs mkdocs 1.0.4
mkdocs-material material 3.0.6
Markdown markdown 3.0.1
pymdown-extensions pymdownx 6.0
jieba jieba 0.39

笔者使用mkdocs-material 4.0.1时遇着大坑,建议制定版本安装3.0.6

目录说明&基础配置

项目目录如下:
图片描述

  • assets:自定义资源

    • css
    • js
    • syntax:语法示例(可忽略)
  • img:存储文档内图片资源
  • label:文档内容
  • index.md:首页

mkdocs.yml配置

site_name: '帮助中心'
site_author: zzm
site_url: http://xxx.com/help/
# 静态资源输出文件夹
site_dir: ../dist/help

# 左侧导航
nav:
- 介绍: index.md
- 标签服务系统帮助文档:
  - 标签平台用户手册: label/user_manual.md
  - 标签数据说明文档: label/data_desc.md
  - 标签需求对接流程: label/demand_process.md
- 统一标签API服务:
  - 0.常见问题:

# 本地调试端口
dev_addr: 127.0.0.1:4050
# 主题配色
theme:
  name: material
  language: 'zh'
  favicon: 'assets/favicon.ico'
  logo:
    icon: ' '
  palette:
    primary: 'Light Blue'
    accent: 'Light Blue'
  feature:
    tabs: false
# 自定义css
extra_css:
- 'assets/css/custom.css'
- 'assets/css/simpleLightbox.min.css'
# 自定义js
extra_javascript:
- 'assets/js/jquery-3.2.1.min.js'
- 'assets/js/simpleLightbox.min.js'
- 'assets/js/custom.js'
# 一些扩展
markdown_extensions:
- markdown.extensions.attr_list
- admonition
- codehilite:
    guess_lang: false
    linenums: false
- toc:
    permalink: true
- footnotes
- meta
- def_list
- pymdownx.arithmatex
- pymdownx.betterem:
    smart_enable: all
- pymdownx.caret
- pymdownx.critic
- pymdownx.details
- pymdownx.inlinehilite
- pymdownx.magiclink
- pymdownx.mark
- pymdownx.smartsymbols
- pymdownx.superfences
- pymdownx.tasklist
- pymdownx.tilde

添加中文搜索支持

目前网上找到的教程都是针对lunr源码替换,但很多都是历史版本的解决方案,随着lunr的更新,很多API已经面目全非,文件夹啥的都对不上,比较懵圈。这里会提供稍微新一点的方案。当然,最终解决方案还是要改造下lunr。

lunr的工作原理可以概括为两步:

  1. 提取页面纯文字版内容到一个json文件,包含锚点位置、标题、描述及标题与描述对应的分词库。大概长这样:
    lunr生成json
  2. 把搜索框输入的内容根据分隔符(空格、标点符号等)切成分词,并和第1步的分词库进行比对,根据对应锚点寻址页面。

实现中文搜索困难的地方在于中文分词的机制和英文不同,不能简单使用分隔符去切词,而中文分词的算法复杂,将所有页面信息临时构建成分词库的效率就会很低。

英文版的lunr现在已经支持日文,对于“帮助文档简介”,可以得到三个分词:帮助,文档,简介。这种机制是,lunr分词是由分隔符导向,同时对词长有一定限制,类似这种汉字过多的成句,只能保留每段分割的前两个字。所以在搜索的时候,成句(一般是大于俩字)目前是搜索不到的,但可以通过空格切割成句进行搜索。

改动如下:

  • 进入python的安装目录修改search_index.py文件

    我的目录在/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/mkdocs/contrib/search/,修改generate_search_index

    def generate_search_index(self):
        """python to json conversion"""
        page_dicts = {
            'docs': self._entries,
            'config': self.config
        }
        for doc in page_dicts['docs']: # 调用jieba的cut接口生成分词库,过滤重复词,过滤空格
            tokens = list(set([token.lower() for token in jieba.cut_for_search(doc['title'].replace('\n', ''), True)]))
            if '' in tokens:
                tokens.remove('')
            doc['title_tokens'] = tokens

            tokens = list(set([token.lower() for token in jieba.cut_for_search(doc['text'].replace('\n', ''), True)]))
            if '' in tokens:
                tokens.remove('')
            doc['text_tokens'] = tokens

        data = json.dumps(page_dicts, sort_keys=True, separators=(',', ':'), ensure_ascii=False)

        if self.config['prebuild_index']:
            try:
                script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'prebuild-index.js')
                p = subprocess.Popen(
                    ['node', script_path],
                    stdin=subprocess.PIPE,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE
                )
                idx, err = p.communicate(data.encode('utf-8'))
                if not err:
                    idx = idx.decode('utf-8') if hasattr(idx, 'decode') else idx
                    page_dicts['index'] = json.loads(idx)
                    data = json.dumps(page_dicts, sort_keys=True, separators=(',', ':'), ensure_ascii=False)
                    log.debug('Pre-built search index created successfully.')
                else:
                    log.warning('Failed to pre-build search index. Error: {}'.format(err))
            except (OSError, IOError, ValueError) as e:
                log.warning('Failed to pre-build search index. Error: {}'.format(e))

        return data
  • 修改lunr.js

    我的目录:/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/mkdocs/contrib/search/templates/search/,搜索lunr.Builder.prototype.add替换部分代码

// 仅替换前15行
lunr.Builder.prototype.add = function (doc, attributes) {
  var docRef = doc[this._ref],
      fields = Object.keys(this._fields)

  this._documents[docRef] = attributes || {}
  this.documentCount += 1

  for (var i = 0; i < fields.length; i++) {
    var fieldName = fields[i],
        extractor = this._fields[fieldName].extractor,
        field = extractor ? extractor(doc) : doc[fieldName],
        tokens = doc[fieldName + '_tokens'],
        terms = this.pipeline.run(tokens),
        fieldRef = new lunr.FieldRef (docRef, fieldName),
        fieldTerms = Object.create(null)

还有一部分需替换

lunr.trimmer = function (token) {
  return token.update(function (s) {
    return s.replace(/^\s+/, '').replace(/\s+$/, '')
  })
}

搞定~ 现在输入中文搜索不到的问题就解决啦

点击图片放大

图片放大又称Lightbox,主要是提供一个浮窗,以展示页面上缩略图的大图版。另一类叫zoom-in,主要是实现鼠标悬停时出现放大镜。目前在markdown中要使用只都能通过外部引入。

首先 下载css及js并引入: Simple lightbox

|---assets
|   |---css
|   |   |----simpleLightbox.min.css
|   |   `----custom.css
|   `---js
|       |----simpleLightbox.min.js
|       `----custom.js

下一步,在配置文件mkdocs.yml中设置extra_css与extra_javascript(如前文配置);
下一步,在custom.css和custom.js中分别添加:

/* custom.css */
a.boxedThumb {
  display: block;
  padding: 4px;
  line-height: 20px;
  border: 1px solid #ddd;
  -webkit-border-radius: 4px;
  -moz-border-radius: 4px;
  border-radius: 4px;
  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
  -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
  -webkit-transition: -webkit-transform .15s ease;
  -moz-transition: -moz-transform .15s ease;
  -o-transition: -o-transform .15s ease;
  -ms-transition: -ms-transform .15s ease;
  transition: transform .15s ease;
}

a.boxedThumb:hover {
  -webkit-transform: scale(1.05);
  -moz-transform: scale(1.05);
  -o-transform: scale(1.05);
  -ms-transform: scale(1.05);
  transform: scale(1.05);
  z-index: 5;
}
$(document).ready(function () {
  let productImageGroups = []
  $('.img-fluid').each(function () {
    let productImageSource = $(this).attr('src')
    let productImageTag = $(this).attr('tag')
    let productImageTitle = $(this).attr('title')
    if (productImageTitle) {
      productImageTitle = 'title="' + productImageTitle + '" '
    }
    else {
      productImageTitle = ''
    }
    $(this).
        wrap('<a class="boxedThumb ' + productImageTag + '" ' +
            productImageTitle + 'href="' + productImageSource + '"></a>')
    productImageGroups.push('.' + productImageTag)
  })
  jQuery.unique(productImageGroups)
  productImageGroups.forEach(productImageGroupsSet)

  function productImageGroupsSet (value) {
    $(value).simpleLightbox()
  }
})

注:此处要确保下述插件开启,它允许在MarkDown链接/图片后用括号指明任意标签的字段。

markdown_extensions:
  - markdown.extensions.attr_list

使用时

![流程图](./../img/user_manual_1.png){.img-fluid tag=1}
# 带说明描述
![流程图](./../img/user_manual_1.png){.img-fluid tag=2 title="测试说明"}
# 图片组
![流程图](./../img/user_manual_1.png){.img-fluid tag=3}
![流程图](./../img/user_manual_1.png){.img-fluid tag=3}
![流程图](./../img/user_manual_1.png){.img-fluid tag=3}

搞定~

阅读 5.1k

The outer world you see is a reflection of your inner self.

88 声望
19 粉丝
0 条评论
你知道吗?

The outer world you see is a reflection of your inner self.

88 声望
19 粉丝
宣传栏