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

57次阅读

共计 6053 个字符,预计需要花费 16 分钟才能阅读完成。

需求

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

最终完成效果如下:左侧导航栏支持多层级嵌套,右侧为当前文档内标题导航。
顶部右侧搜索栏(目前只支持分词搜索,不支持整句):
图片放大:
实现
更全面的信息请移步官网: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 的工作原理可以概括为两步:

提取页面纯文字版内容到一个 json 文件,包含锚点位置、标题、描述及标题与描述对应的分词库。大概长这样:

把搜索框输入的内容根据分隔符(空格、标点符号等)切成分词,并和第 1 步的分词库进行比对,根据对应锚点寻址页面。

实现中文搜索困难的地方在于中文分词的机制和英文不同,不能简单使用分隔符去切词,而中文分词的算法复杂,将所有页面信息临时构建成分词库的效率就会很低。
英文版的 lunr 现在已经支持日文,对于“帮助文档简介”,可以得到三个分词:帮助,文档,简介。这种机制是,lunr 分词是由分隔符导向,同时对词长有一定限制,类似这种汉字过多的成句,只能保留每段分割的前两个字。所以在搜索的时候,成句(一般是大于俩字)目前是搜索不到的,但可以通过空格切割成句进行搜索。
改动如下:
进入 python 的安装目录(安装路径可能不一样,得找找),修改 /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/mkdocs/contrib/search/search_index.py 中的 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.js,搜索 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}
搞定~

正文完
 0