仿MSDN的帮助系统

作为软件开发人员,软件做好后,接下来就是编写文档。我自己也是做软件的,经常有用户询问软件的安装与使用,

我一直很喜欢微软的MSDN帮助系统,简介、大气,使用方便。

网上也找了很久,感觉一直没有合适的,

所以,花了一周时间写了一个仿MSDN的系统 kbase(演示见  http://demo.dotnetcms.org/kbase)。

这篇文章来分析在制作这个系统遇到的几个问题以及总结。

 

 如果分析微软MSDN,不难发现,他的左边是一个导航树,中间是正文,右边是正文的快速链接。

(1)树导航

在选择导航树时,我最初考虑的是bootstrap-treeview

不管从界面还是功能上,他和MSDN的帮助系统导航树都很像。

但是,如果我们仔细观察MSDN,他的导航树里,当点击父节点时是展开子节点,

只有点击“子节点”才能才会打开正文(参考下图)。

但是,这个功能bootstrap treeview控制的并不是很好。

bootstrap treeview点击父节点是打开文字,只有点击父节点前面的“+”才会展开子节点。

 

 

 

所以,最终选择了zTree,在zTree官方给的Demo里,有一个Outlook风格的模板,简直就是MSDN的原型。

同时,ztree提供的接口和功能远远比bootstrap-treeview强大,所以很快我就选中了她。

 

要使用zTree,首先定义zTree的配置seeting,然后在代码里,重新了onClick事件。

在onClick事件里,判断用户点击的节点有没有子节点,

如果有子节点,就展开子节点,

如果没有子节点,就打开网址。

复制代码
var setting = {
    view: {
        showLine: false,
        showIcon: false,
        selectedMulti: false,
        dblClickExpand: false,
        addDiyDom: addDiyDom
    },
    data: {
        simpleData: {
            enable: true
        }
    },

    callback: {
        onClick: onClick
    }
};



function onClick(e, treeId, treeNode) {
     

    if (treeNode.children != null) {
        var zTree = $.fn.zTree.getZTreeObj("treeNav");
        zTree.expandNode(treeNode);
    }
    else {
        if (treeNode.id > 0) {
        }
        else { 
            window.location = "default.aspx?id=" + treeNode.id+"&title="+treeNode.name;
        }
    }
}

function addDiyDom(treeId, treeNode) {
    var spaceWidth = 5;
    var switchObj = $("#" + treeNode.tId + "_switch"),
    icoObj = $("#" + treeNode.tId + "_ico");
    switchObj.remove();
    icoObj.before(switchObj);

    if (treeNode.level >= 1) {
        var spaceStr = "<span style='display: inline-block;width:" + (spaceWidth * treeNode.level) + "px'></span>";
        switchObj.before(spaceStr);
    }
}
复制代码

 

(2)导航树的子节点是类别还是标题

但是,这样又遇到第二个问题:

左边导航树里的内容,到底是按照“分类”处理还是按照“新闻标题”处理。

开发过CMS(内容管理系统)的朋友都知道,一般CMS系统都是:

建立一个catetory分类,然后新闻会有一个标题title,和一个分类catID和分类表关联。

那MSDN左边导航树,用户点击的具体内容到底算“类别”还是“新闻标题”呢?

我最初的想法是算类别,左边设想的是一张表,里面存储所有的导航树“节点”,但是你能想象出来,

如果文章内容越来越多,构建导航树维护是比较困难。

最终,我决定,MSDN导航树,父节点都是“类别”,而叶节点才是新闻的标题,

系统实现里,建了两张表,这两张表通过 SQL的union all 关键字进行合并,然后作为数据源提供给zTree

select id, pId, catname as name,  orderid  from kb_cat 
union all
select     id, catid as pId, title as name,   orderid  from kb_news 

 

 

(3)编辑器选择

 在一开始,我就定位了后台编辑器必须使用Markdown(MD)编辑器,

原因很简单,这个仿MSDN的系统更偏向知识的传递,注重的是内容而不是界面,

而传统的HTML编写帮助文档,会产生大量的“垃圾代码”,这不是我想要的。

因此,接下来就是开始寻找MD编辑器。

我最先想到的是博客园后台有markdown编辑器,所以看看博客园怎么做的吧

但是很快放弃使用博客园的MD了,原因有2点:

(1)他的编写和预览是分开的,这不是我想要的,我希望左边是录入页面,右边是实时预览。

(2)View Source页面,天,博客园的源代码是全压缩的JS, 完全看不到他引用了哪些JS。

算了,放弃把

 

接下来就是痛苦的寻找过程。

包括  几款主流好用的markdown编辑器介绍 等,但是,都感觉差了那么点意思,不是我想要的。

最终,皇天不负有心人,无意中点到了“叶子岛”(叛道)网站,

然后看到了一个“ipandao(爱叛道)”的MD编辑器。

(题外话,这个编辑器名字太难记了,所以,软件开发还是要取一个容易记忆的名字。)

 

点进去看看 https://pandao.github.io/editor.md/ 界面美观,大方,左右布局,提供实时预览,提供自定义扩展,

而且内置和codemirror集成,支持代码高亮显示,而且还开源,源代码的JS里也没有使用太复杂的技术。

 这正是我想要的,这真的是太好了。

 

 

很快,我就把这个系统集成到仿MSDN的系统里。同时,自定义了工具栏,移除了没用的,只保留10来个按钮。

下面是代码的实现 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script type="text/javascript">
    var testEditor;
 
    $(function () {
        testEditor = editormd("test-editormd", {
            width: "100%",
            height:520,
            placeholder: "",
            lineNumbers:true,
            path: "../javascript/pandao-markdown/lib/",
            toolbarIcons: function () {
                return ["bold", "quote", "|", "h3", "h4", "h5", "h6", "list-ul", "list-ol", "|", "link", "image", "|", "code", "code-block", "table"]
            },
 
            imageUpload: true,
            imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
            imageUploadURL: "save_img.aspx",
            saveHTMLToTextarea : true                  
 
        });
    });
</script>

  

 (4)新闻内容的存储

在上一步使用了ipandao的md后,他提供了2个方法用来获取录入的内容:

testEditor.getMarkdown();       // 获取 Markdown 源码
testEditor.getHTML();           // 获取 Textarea 保存的 HTML 源码

接下来一个问题,用户写好的MD以怎么的格式存储到MSSQL里?

如果直接存储HTML,那么后期用户将无法编辑文章,因为MD无法直接加载HTML,他识别不了HTML。

如果直接存储MD,那么浏览器并不能识别MD,这就要用户访问帮助文档时,每次都JS解析MD,可能用户感觉速度会没那么快。

如果同时存储HTML和MD,前端浏览器显示用HTML,后台,用户更新时用MD,通过程序保存数据同步,这会占用数据库空间。

总之,基本上没有完美的解决方法。

在我的做法里,是直接存储MD源代码,然后前端用户访问时,利用MD提供的markdownToHTML方法,转换为HTML进行渲染。

我看了MSDN,猜测他应该是MD和HTML同时存储。

 testEditor.markdownToHTML("contents");

 

(5)右边快速预览

右边快速预览其实是从MD里提取<h3>标题实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$(function () {
    editormd.markdownToHTML("contents");
    let menu = "<h5>本页内容</h4>";
    $("#contents h3").each(
        function () {
            menu += "<a class='quick_a' href='#" + $(this).text() + "'>" + $(this).text() + "</a>";
        }
        );
 
    $("#rightmenu").html(menu);
    $("#rightmenu").pin({
        minWidth: 800,
        padding: { top: 50 }
    });
 
});

  

在上面,还是用了一个插件:jquery.pin()。

他的作用但是:当页面过长,往下滚动时,他会把右边导航“钉”在右上角。

 

这样,一个仿MSDN核心功能介绍完了,剩下的就是普通的数据库增删读写了。

本系统演示见  http://demo.dotnetcms.org/kbase 用户名 admin,密码123456

posted @   启明星工作室  阅读(603)  评论(4编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2018-03-15 使用ASP.NET+Jquery DataTables的服务器分页
2006-03-15 ASP.NET2.0快速入门系列----分层数据(阶梯/等级形)
点击右上角即可分享
微信分享提示