主要添加文集导出功能和优化文集目录显示

This commit is contained in:
yangjian 2019-12-08 18:59:49 +08:00
parent 999f13cf98
commit 4f7de105b1
17 changed files with 1062 additions and 87 deletions

View File

@ -1,6 +1,15 @@
## 版本更新记录
### 2019-12-01
### v0.2.3 2019-12-08
- 优化Markdown内容目录样式
- 修复文档修改时间不生效的Bug
- 添加文档左侧目录可展开收缩功能,默认只显示第一级目录;
- 调整文集页显示逻辑,默认显示文集简介和文集目录大纲;
- 添加用户后台的文集导出功能支持导出文集的Markdown文件压缩包
### v0.2.2 2019-12-01
- 添加MrDoc全站favicon图标
- 添加后台配置关闭注册功能、启用邮箱功能、统计代码、广告代码等功能

View File

@ -1,6 +1,6 @@
# MrDoc - 一个简单的文档写作应用
![Mrdoc首页](./docs/mrdoc_index.png)
![Mrdoc首页](./docs/mrdoc_2019080101.gif)
## 介绍
一个简单的MarkDown文档写作系统。
@ -35,7 +35,61 @@ Markdown科学公式Katex.js
## 安装教程
详见MrDoc使用文档:http://mrdoc.zmister.com
### 1、安装依赖库
```
pip install -r requirements.txt
```
### 2、配置数据库信息
默认情况下MrDoc使用Django的SQLite数据库如果你使用的是MrDoc源码附带的Sqlite数据库则无需另外配置数据库。
如果有配置其他数据库的需求,请在/MrDoc/MrDoc目录下打开settings.py文件在约80行的位置将如下代码
```
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
```
按照自己数据库的信息将其修改如下格式下面以MySQL为例
```
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 使用的数据库后端
'NAME': 'mrdoc', # 数据库名
'USER':'root', # 数据库用户
'PASSWORD':'123456789', # 数据库用户密码
'HOST':'', # 数据库主机地址
'PORT':'3306', # 数据库端口
}
}
```
### 3、初始化数据库
在安装完所需的第三方库并配置好数据库信息之后,我们需要对数据库进行初始化。
在项目路径下打开命令行界面,运行如下命令生成数据库迁移:
```
python manage.py makemigrations
```
接着,运行如下命令执行数据库迁移:
```
python manage.py migrate
```
执行完毕之后,数据库就初始化完成了。
### 4、创建管理员账户
在初始化完数据库之后需要创建一个管理员账户来管理整个MrDoc在项目路径下打开命令行终端运行如下命令
```
python manage.py createsuperuser
```
按照提示输入用户名、电子邮箱地址和密码即可。
### 5、测试运行
在完成上述步骤之后即可运行使用MrDoc。
在测试环境中可以使用Django自带的服务器运行MrDoc其命令为
```
python manage.py runserver
```
## 使用说明
@ -61,7 +115,7 @@ Markdown科学公式Katex.js
## 版本更新
关注州的先生微信公众号IDzmister2016、博客 https://zmister.com,及时获取MrDoc版本更新信息。
关注州的先生微信公众号IDzmister2016、博客 https://zmister.com及时获取MrDoc版本更新信息。
## 更多截图

122
app_doc/report_utils.py Normal file
View File

@ -0,0 +1,122 @@
# coding:utf-8
# @文件: report_utils.py
# @创建者:州的先生
# #日期2019/12/7
# 博客地址zmister.com
# MrDoc文集文档导出相关功能代码
from django.conf import settings
from app_doc.models import *
import subprocess
import datetime
import re
import os
import shutil
# 导出MD文件压缩包
class ReportMD():
def __init__(self,project_id):
# 查询文集信息
self.pro_id = project_id
project_data = Project.objects.get(pk=project_id)
# 文集名称
self.project_name = "{0}_{1}_{2}".format(
project_data.create_user,
project_data.name,
str(datetime.date.today())
)
# 判断MD导出临时文件夹是否存在
if os.path.exists(settings.MEDIA_ROOT + "/reportmd_temp") is False:
os.mkdir(settings.MEDIA_ROOT + "/reportmd_temp")
# 判断文集名称文件夹是否存在
self.project_path = settings.MEDIA_ROOT + "/reportmd_temp/{}".format(self.project_name)
is_fold = os.path.exists(self.project_path)
if is_fold is False:
os.mkdir(self.project_path)
# 判断是否存在静态文件文件夹
self.media_path = settings.MEDIA_ROOT + "/reportmd_temp/{}/media".format(self.project_name)
is_media = os.path.exists(self.media_path)
if is_media is False:
os.mkdir(self.media_path)
def work(self):
# 读取指定文集的文档数据
data = Doc.objects.filter(top_doc=self.pro_id, parent_doc=0).order_by("sort")
# 遍历文档
for d in data:
md_name = d.name
md_content = d.pre_content
md_content = self.operat_md_media(md_content)
# 新建MD文件
with open('{}/{}.md'.format(self.project_path,md_name),'w',encoding='utf-8') as files:
files.write(md_content)
# 查询二级文档
data_2 = Doc.objects.filter(parent_doc=d.id).order_by("sort")
for d2 in data_2:
md_name_2 = d2.name
md_content_2 = d2.pre_content
md_content_2 = self.operat_md_media(md_content_2)
# 新建MD文件
with open('{}/{}.md'.format(self.project_path, md_name_2), 'w', encoding='utf-8') as files:
files.write(md_content_2)
# 获取第三级文档
data_3 = Doc.objects.filter(parent_doc=d2.id).order_by("sort")
for d3 in data_3:
md_name_3 = d3.name
md_content_3 = d3.pre_content
md_content_3 = self.operat_md_media(md_content_3)
# 新建MD文件
with open('{}/{}.md'.format(self.project_path, md_name_3), 'w', encoding='utf-8') as files:
files.write(md_content_3)
# 压缩文件
shutil.make_archive(self.project_path,'zip',self.project_path)
# 删除文件夹
shutil.rmtree(self.project_path)
return "{}.zip".format(self.project_path)
# 处理MD内容中的静态文件
def operat_md_media(self,md_content):
# 查找MD内容中的静态文件
pattern = r"\!\[.*?\]\(.*?\)"
media_list = re.findall(pattern, md_content)
# print(media_list)
# 存在静态文件,进行遍历
if len(media_list) > 0:
for media in media_list:
media_filename = media.split("(")[-1].split(")")[0] # 媒体文件的文件名
# 对本地静态文件进行复制
if media_filename.startswith("/"):
sub_folder = "/" + media_filename.split("/")[3] # 获取子文件夹的名称
is_sub_folder = os.path.exists(self.media_path+sub_folder)
# 创建子文件夹
if is_sub_folder is False:
os.mkdir(self.media_path+sub_folder)
# 替换MD内容的静态文件链接
md_content = md_content.replace(media_filename, "." + media_filename)
# 复制静态文件到指定文件夹
shutil.copy(settings.BASE_DIR + media_filename, self.media_path+sub_folder)
# 不存在本地静态文件直接返回MD内容
# else:
# print("没有本地静态文件")
return md_content
# 不存在静态文件直接返回MD内容
else:
return md_content
if __name__ == '__main__':
app = ReportMD(
project_id=7
)
app.work()

View File

@ -10,6 +10,7 @@ urlpatterns = [
path('modify_pro/',views.modify_project,name='modify_project'), # 修改文集
path('manage_project',views.manage_project,name="manage_project"), # 管理文集
path('del_project/',views.del_project,name='del_project'), # 删除文集
path('report_project_md/',views.report_md,name='report_md'), # 导出文集MD文件
#################文档相关
path('project/<int:pro_id>/<int:doc_id>/', views.doc, name='doc'), # 文档浏览页
path('create_doc/', views.create_doc, name="create_doc"), # 新建文档

View File

@ -5,6 +5,9 @@ from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage,InvalidPa
from app_doc.models import Project,Doc,DocTemp
from django.contrib.auth.models import User
from django.db.models import Q
import datetime
import traceback
from app_doc.report_utils import *
# 文集列表
@ -36,7 +39,7 @@ def create_project(request):
return JsonResponse({'status':False})
# 文集浏览
# 文集
def project_index(request,pro_id):
# 获取文集
if request.method == 'GET':
@ -45,19 +48,16 @@ def project_index(request,pro_id):
project = Project.objects.get(id=int(pro_id))
# 获取搜索词
kw = request.GET.get('kw','')
if kw == '':
doc = Doc.objects.filter(top_doc=int(pro_id)).order_by('sort')
# 获取文集第一篇文档作为默认内容
if doc.count() > 0:
doc = doc[0]
else:
doc = None
else: # 搜索结果
search_result = Doc.objects.filter(top_doc=int(pro_id),pre_content__icontains=kw)
# 获取文集下所有一级文档
project_docs = Doc.objects.filter(top_doc=int(pro_id), parent_doc=0).order_by('sort')
return render(request,'app_doc/project.html',locals())
if kw != '':
search_result = Doc.objects.filter(top_doc=int(pro_id),pre_content__icontains=kw)
# if search_result.count() == 0:
# search_result = {'count':0}
return render(request,'app_doc/project_doc_search.html',locals())
return render(request, 'app_doc/project.html', locals())
except Exception as e:
print(traceback.print_exc())
return HttpResponse('请求出错')
else:
return HttpResponse('方法不允许')
@ -152,7 +152,7 @@ def doc(request,pro_id,doc_id):
doc = Doc.objects.get(id=int(doc_id))
# 获取文集下一级文档
project_docs = Doc.objects.filter(top_doc=doc.top_doc, parent_doc=0).order_by('sort')
return render(request,'app_doc/project.html',locals())
return render(request,'app_doc/doc.html',locals())
else:
return HttpResponse('参数错误')
except Exception as e:
@ -233,6 +233,7 @@ def modify_doc(request,doc_id):
pre_content=pre_content,
parent_doc=int(parent_doc) if parent_doc != '' else 0,
sort=sort if sort != '' else 99,
modify_time = datetime.datetime.now()
)
return JsonResponse({'status': True,'data':'修改成功'})
else:
@ -448,3 +449,28 @@ def get_pro_doc(request):
# 404页面
def handle_404(request):
return render(request,'404.html')
# 导出文集MD文件
@login_required()
def report_md(request):
if request.method == 'POST':
pro_id = request.POST.get('project_id','')
user = request.user
try:
project = Project.objects.get(id=int(pro_id))
if project.create_user == user:
project_md = ReportMD(
project_id=int(pro_id)
)
md_file_path = project_md.work() # 生成并获取MD文件压缩包绝对路径
md_file_filename = os.path.split(md_file_path)[-1] # 提取文件名
md_file = "/media/reportmd_temp/"+ md_file_filename # 拼接相对链接
return JsonResponse({'status':True,'data':md_file})
else:
return JsonResponse({'status':False,'data':'无权限'})
except:
return JsonResponse({'status':False,'data':'文集不存在'})
else:
return Http404

Binary file not shown.

BIN
docs/mrdoc_2019080101.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

View File

@ -250,7 +250,7 @@ body, html {
.doc-summary ul.summary li a:hover,.bq a:hover{
text-decoration: underline;
}
li.active > a{
li.active > a,li.active > div > a{
color: #008cff;
}
.bq a {
@ -261,6 +261,7 @@ li.active > a{
border-top: 1px solid #dcdcdc;
margin-top: 10px;
font-size: 12px;
text-align:center;
}
.doc-header {
font-family: helvetica neue,Helvetica,Arial,sans-serif;
@ -353,4 +354,12 @@ li.active > a{
/* 广告样式 */
.ad-code{
margin:10px;
}
/* 左边侧栏目录样式 */
.toc-open{
display:block;
}
.toc-close{
display:none;
}

View File

@ -31,6 +31,7 @@
</a>
<dl class="layui-nav-child">
<!-- <dd><a href="">基本资料</a></dd> -->
<dd><a href="{% url 'manage_project' %}">个人中心</a></dd>
<dd><a href="{% url 'logout' %}">退出登录</a></dd>
</dl>
</li>
@ -54,7 +55,7 @@
<a href="{% url 'user_manage' %}"><i class="layui-icon layui-icon-user"></i> 用户管理</a>
</li>
<li class="layui-nav-item layui-nav-itemed">
<a href="{% url 'sys_setting' %}"><i class="layui-icon layui-icon-user"></i> 应用设置</a>
<a href="{% url 'sys_setting' %}"><i class="layui-icon layui-icon-set-sm"></i> 应用设置</a>
</li>
{% endif %}
</ul>

View File

@ -43,9 +43,9 @@
{% for pro in projects %}
<tr>
<td>{{ pro.name }}</td>
<td>{{ pro.intro }}</td>
{% load project_filter %}
<td>{{ pro.id | get_doc_count }}</td>
<td>{{ pro.intro }}</td>
{% load project_filter %}
<td>{{ pro.id | get_doc_count }}</td>
<td>{{ pro.create_time }}</td>
<td>{{ pro.create_user }}</td>
<td>

View File

@ -57,6 +57,7 @@
</form>
</div>
<!-- 网站设置结束 -->
<!-- 邮箱设置 -->
<div class="layui-tab-item">
<form class="layui-form" action="" method="post">

View File

@ -85,7 +85,13 @@
}
}
});
//未保存离开提示
window.onbeforeunload =function() {
   // code...
//return null;
console.log("xx")
return 1
}
</script>
<script src="{% static 'mrdoc.js' %}"></script>
{% block custom_script %}

345
template/app_doc/doc.html Normal file
View File

@ -0,0 +1,345 @@
{% load staticfiles %}
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>{{ doc.name }} - {{ project.name }} - MrDoc</title>
<link href="{% static 'layui/css/layui.css' %}" rel="stylesheet">
<link rel="stylesheet" href="{% static 'editor.md/css/editormd.css' %}" />
<link rel="stylesheet" href="{% static 'prism/prism.css' %}" />
<link rel="stylesheet" href="{% static 'katex/katex.min.css' %}" />
<link rel="stylesheet" href="{% static 'share.js/css/share.min.css' %}" />
<link rel="icon" href="{% static 'favicon_16.png' %}"/>
<link href="{% static 'mrdoc.css' %}" rel="stylesheet">
<style>
.doc-content ul li{
list-style:disc;
}
.doc-content ul > li > ul > li{
list-style-type: circle;
}
.doc-content ol li{
list-style-type: decimal;
}
</style>
</head>
<body>
<div class="doc">
<!-- 左侧目录栏 -->
<div class="doc-summary">
<form action="{% url 'pro_index' pro_id=project.id %}" method="get">
<div id="doc-search-input">
<input type="text" name="kw" placeholder="输入并回车搜索" value="" class="layui-input doc-search-input">
</div>
</form>
<div class="project-title">
<a href="{% url 'pro_index' pro_id=project.id %}">{{ project.name }}</a>
</div>
<hr>
{% load doc_filter %}
<nav>
<ul class="summary">
<!-- 一级目录 -->
{% for docs in project_docs %}
<li>
{% if docs.id|get_next_doc %}
<div style="display:flex;justify-content:space-between;">
<a href="{% url 'doc' pro_id=docs.top_doc doc_id=docs.id %}">{{ docs.name }}</a>
<i class="fa fa-chevron-left switch-toc" style="padding:15px;"></i>
</div>
<ul class="sub-menu toc-close">
<!-- 二级目录 -->
{% for node in docs.id|get_next_doc %}
<li>
{% if node.id|get_next_doc %}
<div style="display:flex;justify-content:space-between;">
<a href="{% url 'doc' pro_id=node.top_doc doc_id=node.id %}">{{ node.name }}</a>
<i class="fa fa-chevron-left switch-toc" style="padding:15px;"></i>
</div>
<ul class="sub-menu toc-close">
<!-- 三级目录 -->
{% for doc in node.id|get_next_doc %}
<li><a href="{% url 'doc' pro_id=doc.top_doc doc_id=doc.id %}">{{ doc.name }}</a></li>
{% endfor %}
</ul>
{% else %}
<a href="{% url 'doc' pro_id=node.top_doc doc_id=node.id %}">{{ node.name }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<a href="{% url 'doc' pro_id=docs.top_doc doc_id=docs.id %}">{{ docs.name }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
</nav>
<div class="bq">
<a href="" class="mrdoc-link">本文档使用MrDoc发布</a>
</div>
</div>
<!-- 左侧目录栏结束 -->
<!-- 右侧文档栏 -->
<div class="doc-body">
<!-- 文档导航 -->
<div class="doc-header" role="navigation">
<a class="btn pull-right" aria-label="" href="{% url 'pro_list' %}">
<i class="fa fa-home"></i> 返回首页
</a>
<a class="btn pull-left js-toolbar-action" aria-label="" href="javascript:void(0);" title="切换侧边栏">
<i class="fa fa-align-justify"></i>
</a>
<a class="btn pull-left font-small" href="javascript:void(0);" title="缩小字体">
<i class="fa fa-font">-</i>
</a>
<div class="dropdown pull-left font-settings ">
<a class="btn toggle-dropdown font-large" href="javascript:void(0);" title="放大字体">
<i class="fa fa-font">+</i>
</a>
</div>
<a class="btn pull-left font-switch" href="javascript:void(0);" title="切换字体类型">
<i class="fa fa-text-height"></i>
</a>
{% if request.user == doc.create_user %}
<a class="btn pull-left" aria-label="" href="{% url 'modify_doc' doc_id=doc.id %}">
<i class="fa fa-edit"></i> 修改文档
</a>
<a class="btn pull-left" aria-label="" href="{% url 'create_doc' %}" target="_blank">
<i class="fa fa-edit"></i> 新建文档
</a>
{% endif %}
</div>
<!-- 文档主体 -->
<div class="doc-body-content">
<div class="doc-body-content-div">
<!-- 文档内容 -->
<div class="doc-content">
<!-- 标题 -->
<div class="doc-info">
<!-- 文档标题 -->
{% if doc %}
<h1>{{ doc.name }}</h1>
<hr>
<p style="color: #666;font-size:12px;">
<i class="fa fa-th-large"></i> 发表:{{ doc.create_time }}
&nbsp;&nbsp;&nbsp;&nbsp;<i class="fa fa-edit"></i> 最后修改:{{ doc.modify_time }}
<!--<i class="fa fa-user"></i> {{ doc.create_user.username }}-->
</p>
{% else %}
<h1>共有{{ search_result.count }}个搜索结果</h1>
<hr>
{% endif %}
<!-- 广告代码开始 -->
{% if ad_code %}
<div class="ad-code">
{{ ad_code | safe }}
</div>
{% endif %}
<!-- 广告代码结束 -->
</div>
<!-- 标题结束 -->
<!-- 正文开始 -->
<div class="markdown-body" id="content">
{% if doc %}
{# {{ doc.content | safe }}#}
<style>
div.editormd-toc-menu ul.markdown-toc-list li,ul.markdown-toc-list > li > ul li{
list-style: none;
}
</style>
<textarea id="" style="display: none;">{{ doc.pre_content }}</textarea>
{% else %}
{% for result in search_result %}
<div>
<h2><a href="{% url 'doc' pro_id=project.id doc_id=result.id %}">{{ result.name }}</a></h2>
<p>{{ result.pre_content|truncatechars:300 }}</p>
</div>
{% endfor %}
{% endif %}
</div>
<!-- 正文结束 -->
</div>
<!-- 文档目录 -->
<div class="doc-cata">
<div id="toc-container"></div>
</div>
<!-- 社交分享 -->
<div class="share-div" style="margin-top: 10px;padding:10px;text-align: center;background-color: #fafafa">
分享到:<span class="social-share"></span>
</div>
</div>
</div>
</div>
<!-- 右侧文档栏结束 -->
<div class="toTop"><i class="layui-icon layui-icon-top" style="font-size: 50px;"></i></div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<script src="{% static 'layui/layui.all.js' %}"></script>
<script src="{% static 'prism/prism.js' %}"></script>
<script src="{% static 'editor.md/lib/marked.min.js' %}"></script>
<script src="{% static 'editor.md/lib/prettify.min.js' %}"></script>
<script src="{% static 'editor.md/lib/raphael.min.js' %}"></script>
<script src="{% static 'editor.md/lib/underscore.min.js' %}"></script>
<script src="{% static 'editor.md/lib/sequence-diagram.min.js' %}"></script>
<script src="{% static 'editor.md/lib/flowchart.min.js' %}"></script>
<script src="{% static 'editor.md/lib/jquery.flowchart.min.js' %}"></script>
<script src="{% static 'editor.md/editormd.min.js' %}"></script>
<script src="{% static 'share.js/js/social-share.min.js' %}"></script>
{% block custom_script %}
<script>
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
//解析Markdown为HTML
editormd.markdownToHTML("content", {
htmlDecode : "style,script,iframe",
emoji : true,
taskList : true,
tex : true, // 科学公式
flowChart : true, // 流程图
sequenceDiagram : true, // 默认不解析
tocm : true, //目录
//tocContainer : "#toc-container"
});
//为当前页面的目录链接添加蓝色样式
$("nav li a").each(function (i) {
var $me = $(this);
var lochref = $.trim(window.location.href);
var mehref = $.trim($me.get(0).href);
if (lochref.indexOf(mehref) != -1) {
//console.log($me,lochref,mehref)
$me.closest("li").addClass("active");
//展开当前文档的上级目录
$me.parent("li").parent('ul.sub-menu').toggleClass("toc-close toc-open"); //展开二级目录
$me.parent("div").parent('li').parent('ul.sub-menu').toggleClass("toc-close toc-open"); //展开还有子级的二级目录
$me.parent("li").parent('ul').parent('li').parent('ul.sub-menu').toggleClass("toc-close toc-open"); //展开三级目录
$me.parents("ul.sub-menu").prevAll("div").children("i").toggleClass("fa-chevron-left fa-chevron-down");//切换图标
} else {
//console.log(lochref,mehref)
$me.closest("li").removeClass("active");
}
});
</script>
<!-- 页面初始化字体设置 -->
<script>
font_stauts = window.localStorage.getItem('font-sans')
//font_size = window.localStorage.getItem('font-size')
if(font_stauts == 'serif'){
$(".doc-content").toggleClass("switch-font")
$("#content").toggleClass("switch-font")
}
if(window.localStorage.getItem('font-size')){
font_size = window.localStorage.getItem('font-size')
console.log(font_size)
//$('.doc-info h1').css({'font-size':font_size+'rem'})
$('#content').css({'font-size':font_size+'rem'})
}else{
window.localStorage.setItem('font-size',0.8)
}
</script>
<!-- 返回顶部 -->
<script type="text/javascript">
$(document).ready(function() {
// 初始时,“返回顶部”标签隐藏
$(".toTop").hide();
$(window).scroll(function() {
// 若滚动的高度,超出指定的高度后,“返回顶部”的标签出现。
if($(document).scrollTop() >= 150) {
$(".toTop").show();
} else {
$(".toTop").hide();
}
})
// 绑定点击事件,实现返回顶部的效果
$(".toTop").click(function() {
$(document).scrollTop(0);
});
});
</script>
<!-- 切换隐藏侧边栏 -->
<script>
// 切换侧边栏
$(function(){
$(".js-toolbar-action").click(toggleSidebar);
});
//切换侧边栏显示隐藏
function toggleSidebar(){
console.log("切换侧边栏")
$("body").toggleClass("big-page");
return false;
}
</script>
<!-- 切换内容字体 -->
<script>
//切换文档内容字体类型
$(function(){
$('.font-switch').click(switchFont);
});
function switchFont(){
if(font_stauts == 'serif'){
$(".doc-content").toggleClass("switch-font")
$("#content").toggleClass("switch-font")
window.localStorage.setItem('font-sans','sans')
}else{
$(".doc-content").toggleClass("switch-font")
$("#content").toggleClass("switch-font")
window.localStorage.setItem('font-sans','serif')
}
};
//放大字体
$(function(){
$('.font-large').click(largeFont);
});
function largeFont(){
var font_size = window.localStorage.getItem('font-size')
console.log(font_size)
if(parseFloat(font_size) < 1.4){
size = parseFloat(font_size) + 0.1
//$('.doc-info h1').css({'font-size':size+'rem'})
$('#content').css({'font-size':size+'rem'})
window.localStorage.setItem('font-size',size)
}else{
console.log("xxx")
}
};
//缩小字体
$(function(){
$('.font-small').click(smallFont);
});
function smallFont(){
var font_size = window.localStorage.getItem('font-size')
//console.log(font_size)
if(parseFloat(font_size) >= 0.6){
size = parseFloat(font_size) - 0.2
//$('.doc-info h1').css({'font-size':size+'rem'})
$('#content').css({'font-size':size+'rem'})
window.localStorage.setItem('font-size',size)
}else{
console.log("xxx")
}
};
</script>
<!-- 展开收起左边目录 -->
<script>
$(function(){
$(".switch-toc").click(SwitchToc);
});
function SwitchToc(i){
var $me = $(this);
$(this).parent().next("ul").toggleClass("toc-close"); //切换展开收起样式
$(this).toggleClass("fa-chevron-left fa-chevron-down");//切换图标
};
</script>
<!-- 统计代码开始 -->
{{ static | safe }}
<!-- 统计代码结束 -->
{% endblock %}
</body>
</html>

View File

@ -34,7 +34,9 @@
<i class="layui-icon layui-icon-username"></i> {{request.user.username}}
</a>
<dl class="layui-nav-child">
<!-- <dd><a href="">基本资料</a></dd> -->
{% if request.user.is_superuser %}
<dd><a href="{% url 'doctemp_manage' %}">后台管理</a></dd>
{% endif %}
<dd><a href="{% url 'logout' %}">退出登录</a></dd>
</dl>
</li>

View File

@ -24,13 +24,15 @@
<colgroup>
<col width="100">
<col width="400">
<col width="50">
<col width="100">
<col width="100">
<col width="150">
</colgroup>
<thead>
<tr>
<th>文集名称</th>
<th>文集简介</th>
<th>文集简介</th>
<th>文档数量</th>
<th>创建时间</th>
<th>操作</th>
</tr>
@ -38,13 +40,16 @@
<tbody>
{% for pro in pros %}
<tr>
<td>{{ pro.name }}</td>
<td>{{ pro.intro }}</td>
<td><a href="{% url 'pro_index' pro_id=pro.id %}" target="_blank">{{ pro.name }}</a></td>
<td>{{ pro.intro }}</td>
{% load project_filter %}
<td>{{ pro.id | get_doc_count }}</td>
<td>{{ pro.create_time }}</td>
<td>
<a href="{% url 'pro_index' pro_id=pro.id %}" target="_blank" class="layui-btn layui-btn-normal layui-btn-xs">查看</a>
<!--<a href="{% url 'pro_index' pro_id=pro.id %}" target="_blank" class="layui-btn layui-btn-normal layui-btn-xs">查看</a>-->
<a href="javascript:void(0);" onclick="modifyProject('{{pro.id}}','{{pro.name}}','{{pro.intro}}')" class="layui-btn layui-btn-warm layui-btn-xs">修改</a>
<a href="javascript:void(0);" onclick="delProject('{{pro.id}}');" class="layui-btn layui-btn-danger layui-btn-xs">删除</a>
<a href="javascript:void(0);" onclick="reportMd('{{pro.id}}')" class="layui-btn layui-btn-normal layui-btn-xs">导出</a>
</td>
</tr>
{% endfor %}
@ -157,5 +162,48 @@
},
});
}
//导出MD
reportMd = function(pro_id){
layer.open({
type:1,
title:"导出文集",
area:"300px",
id:"reportMd",
content:'<div style="margin-left:10px;">将此文集下所有文档导出为MD文件并打包</div>',
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
var load = layer.load()
data = {
'project_id':pro_id,
}
$.post("{% url 'report_md' %}",data,function(r){
if(r.status){
//导出成功
layer.close(index)
//文件下载提示
downloadMd(r.data)
}else{
//导出失败,提示
console.log(r)
layer.msg(r.data)
}
layer.close(load)
})
},
})
}
//下载文件弹出框
downloadMd = function(download_link){
layer.open({
type:1,
title:"下载导出文档",
area:"300px",
id:"downloadMd",
content:'<p style="text-align:center;color:red;">请尽快下载,避免失效!</p><br><a class="layui-btn layui-btn-fluid" href="'+ download_link + '" download="mrdoc_report_md.zip" >点击下载文件(zip)</a>',
//btn:['确定','取消'], //添加按钮
//btnAlign:'c', //按钮居中
})
};
</script>
{% endblock %}

View File

@ -3,7 +3,7 @@
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>{{ doc.name }} - {{ project.name }} - MrDoc</title>
<title>{{ project.name }} - MrDoc</title>
<link href="{% static 'layui/css/layui.css' %}" rel="stylesheet">
<link rel="stylesheet" href="{% static 'editor.md/css/editormd.css' %}" />
<link rel="stylesheet" href="{% static 'prism/prism.css' %}" />
@ -42,25 +42,35 @@
<!-- 一级目录 -->
{% for docs in project_docs %}
<li>
<a href="{% url 'doc' pro_id=docs.top_doc doc_id=docs.id %}">{{ docs.name }}</a>
{% if docs.id|get_next_doc %}
{# <i class="layui-icon layui-icon-down tagger-on"></i>#}
<ul class="sub-menu">
<!-- 二级目录 -->
{% for node in docs.id|get_next_doc %}
<li><a href="{% url 'doc' pro_id=node.top_doc doc_id=node.id %}">{{ node.name }}</a>
{% if node.id|get_next_doc %}
<ul>
<!-- 三级目录 -->
{% for doc in node.id|get_next_doc %}
<li><a href="{% url 'doc' pro_id=doc.top_doc doc_id=doc.id %}">{{ doc.name }}</a></li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% if docs.id|get_next_doc %}
<div style="display:flex;justify-content:space-between;">
<a href="{% url 'doc' pro_id=docs.top_doc doc_id=docs.id %}">{{ docs.name }}</a>
<i class="fa fa-chevron-left switch-toc" style="padding:15px;"></i>
</div>
<ul class="sub-menu toc-close">
<!-- 二级目录 -->
{% for node in docs.id|get_next_doc %}
<li>
{% if node.id|get_next_doc %}
<div style="display:flex;justify-content:space-between;">
<a href="{% url 'doc' pro_id=node.top_doc doc_id=node.id %}">{{ node.name }}</a>
<i class="fa fa-chevron-left switch-toc" style="padding:15px;"></i>
</div>
<ul class="sub-menu toc-close">
<!-- 三级目录 -->
{% for doc in node.id|get_next_doc %}
<li><a href="{% url 'doc' pro_id=doc.top_doc doc_id=doc.id %}">{{ doc.name }}</a></li>
{% endfor %}
</ul>
{% else %}
<a href="{% url 'doc' pro_id=node.top_doc doc_id=node.id %}">{{ node.name }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<a href="{% url 'doc' pro_id=docs.top_doc doc_id=docs.id %}">{{ docs.name }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
@ -109,17 +119,15 @@
<div class="doc-content">
<!-- 标题 -->
<div class="doc-info">
{% if doc %}
<h1>{{ doc.name }}</h1>
<!-- 文集标题 -->
<h1>{{ project.name }}</h1>
<hr>
<p style="color: #666;">
<i class="layui-icon layui-icon-date"></i> {{ doc.create_time }}&nbsp;&nbsp;&nbsp;&nbsp;
<i class="layui-icon layui-icon-user"></i> {{ doc.create_user.username }}
<p style="color: #666;font-size:12px;">
<i class="fa fa-th-large"></i> 发表:{{ project.create_time }}
&nbsp;&nbsp;&nbsp;&nbsp;<i class="fa fa-edit"></i> 最后修改:{{ project.modify_time }}
<!--<i class="fa fa-user"></i> {{ doc.create_user.username }}-->
</p>
{% else %}
<h1>共有{{ search_result.count }}个搜索结果</h1>
<hr>
{% endif %}
<!-- 广告代码开始 -->
{% if ad_code %}
<div class="ad-code">
@ -129,24 +137,46 @@
<!-- 广告代码结束 -->
</div>
<!-- 标题结束 -->
<!-- 正文开始 -->
<div class="markdown-body" id="content">
{% if doc %}
{# {{ doc.content | safe }}#}
<style>
ul.markdown-toc-list li,ul.markdown-toc-list > li > ul li{
list-style: none;
}
</style>
<textarea id="" style="display: none;">{{ doc.pre_content }}</textarea>
{% else %}
{% for result in search_result %}
<div>
<h2><a href="{% url 'doc' pro_id=project.id doc_id=result.id %}">{{ result.name }}</a></h2>
<p>{{ result.pre_content|truncatechars:300 }}</p>
</div>
<div class="layui-row">
<h3>文集简介:</h3>
<p>{{project.intro}}</p>
</div>
<div class="layui-row">
<h3>文集大纲:</h3>
<ul class="summary">
<!-- 一级目录 -->
{% for docs in project_docs %}
<li>
{% if docs.id|get_next_doc %}
<a href="{% url 'doc' pro_id=docs.top_doc doc_id=docs.id %}">{{ docs.name }}</a>
<ul class="sub-menu">
<!-- 二级目录 -->
{% for node in docs.id|get_next_doc %}
<li>
{% if node.id|get_next_doc %}
<a href="{% url 'doc' pro_id=node.top_doc doc_id=node.id %}">{{ node.name }}</a>
<ul class="sub-menu">
<!-- 三级目录 -->
{% for doc in node.id|get_next_doc %}
<li><a href="{% url 'doc' pro_id=doc.top_doc doc_id=doc.id %}">{{ doc.name }}</a></li>
{% endfor %}
</ul>
{% else %}
<a href="{% url 'doc' pro_id=node.top_doc doc_id=node.id %}">{{ node.name }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<a href="{% url 'doc' pro_id=docs.top_doc doc_id=docs.id %}">{{ docs.name }}</a>
{% endif %}
</li>
{% endfor %}
{% endif %}
</ul>
</div>
</div>
<!-- 正文结束 -->
</div>
@ -183,28 +213,22 @@
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
//解析Markdown为HTML
editormd.markdownToHTML("content", {
htmlDecode : "style,script,iframe",
emoji : true,
taskList : true,
tex : true, // 科学公式
flowChart : true, // 流程图
sequenceDiagram : true, // 默认不解析
tocm : true, //目录
//tocContainer : "#toc-container"
});
//为当前页面的目录链接添加样式
//为当前页面的目录链接添加蓝色样式
$("nav li a").each(function (i) {
var $me = $(this);
var lochref = $.trim(window.location.href);
var mehref = $.trim($me.get(0).href);
if (lochref.indexOf(mehref) != -1) {
//console.log($me,lochref,mehref)
$me.parent().addClass("active");
$me.closest("li").addClass("active");
//展开当前文档的上级目录
$me.parent("li").parent('ul.sub-menu').toggleClass("toc-close toc-open"); //展开二级目录
$me.parent("div").parent('li').parent('ul.sub-menu').toggleClass("toc-close toc-open"); //展开还有子级的二级目录
$me.parent("li").parent('ul').parent('li').parent('ul.sub-menu').toggleClass("toc-close toc-open"); //展开三级目录
$me.parents("ul.sub-menu").prevAll("div").children("i").toggleClass("fa-chevron-left fa-chevron-down");//切换图标
} else {
//console.log(lochref,mehref)
$me.parent().removeClass("active");
$me.closest("li").removeClass("active");
}
});
</script>
@ -282,7 +306,7 @@
var font_size = window.localStorage.getItem('font-size')
console.log(font_size)
if(parseFloat(font_size) < 1.4){
size = parseFloat(font_size) + 0.2
size = parseFloat(font_size) + 0.1
//$('.doc-info h1').css({'font-size':size+'rem'})
$('#content').css({'font-size':size+'rem'})
window.localStorage.setItem('font-size',size)
@ -307,6 +331,18 @@
}
};
</script>
<!-- 展开收起左边目录 -->
<script>
$(function(){
$(".switch-toc").click(SwitchToc);
});
function SwitchToc(i){
var $me = $(this);
$(this).parent().next("ul").toggleClass("toc-close"); //切换展开收起样式
$(this).toggleClass("fa-chevron-left fa-chevron-down");//切换图标
};
</script>
<!-- 统计代码开始 -->
{{ static | safe }}
<!-- 统计代码结束 -->

View File

@ -0,0 +1,315 @@
{% load staticfiles %}
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>搜索{{kw}}结果 - {{ project.name }} - MrDoc</title>
<link href="{% static 'layui/css/layui.css' %}" rel="stylesheet">
<link rel="stylesheet" href="{% static 'editor.md/css/editormd.css' %}" />
<link rel="stylesheet" href="{% static 'prism/prism.css' %}" />
<link rel="stylesheet" href="{% static 'katex/katex.min.css' %}" />
<link rel="stylesheet" href="{% static 'share.js/css/share.min.css' %}" />
<link rel="icon" href="{% static 'favicon_16.png' %}"/>
<link href="{% static 'mrdoc.css' %}" rel="stylesheet">
<style>
.doc-content ul li{
list-style:disc;
}
.doc-content ul > li > ul > li{
list-style-type: circle;
}
.doc-content ol li{
list-style-type: decimal;
}
</style>
</head>
<body>
<div class="doc">
<!-- 左侧目录栏 -->
<div class="doc-summary">
<form action="{% url 'pro_index' pro_id=project.id %}" method="get">
<div id="doc-search-input">
<input type="text" name="kw" placeholder="输入并回车搜索" value="" class="layui-input doc-search-input">
</div>
</form>
<div class="project-title">
<a href="{% url 'pro_index' pro_id=project.id %}">{{ project.name }}</a>
</div>
<hr>
{% load doc_filter %}
<nav>
<ul class="summary">
<!-- 一级目录 -->
{% for docs in project_docs %}
<li>
{% if docs.id|get_next_doc %}
<div style="display:flex;justify-content:space-between;">
<a href="{% url 'doc' pro_id=docs.top_doc doc_id=docs.id %}">{{ docs.name }}</a>
<i class="fa fa-chevron-left switch-toc" style="padding:15px;"></i>
</div>
<ul class="sub-menu toc-close">
<!-- 二级目录 -->
{% for node in docs.id|get_next_doc %}
<li>
{% if node.id|get_next_doc %}
<div style="display:flex;justify-content:space-between;">
<a href="{% url 'doc' pro_id=node.top_doc doc_id=node.id %}">{{ node.name }}</a>
<i class="fa fa-chevron-left switch-toc" style="padding:15px;"></i>
</div>
<ul class="sub-menu toc-close">
<!-- 三级目录 -->
{% for doc in node.id|get_next_doc %}
<li><a href="{% url 'doc' pro_id=doc.top_doc doc_id=doc.id %}">{{ doc.name }}</a></li>
{% endfor %}
</ul>
{% else %}
<a href="{% url 'doc' pro_id=node.top_doc doc_id=node.id %}">{{ node.name }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<a href="{% url 'doc' pro_id=docs.top_doc doc_id=docs.id %}">{{ docs.name }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
</nav>
<div class="bq">
<a href="" class="mrdoc-link">本文档使用MrDoc发布</a>
</div>
</div>
<!-- 左侧目录栏结束 -->
<!-- 右侧文档栏 -->
<div class="doc-body">
<!-- 文档导航 -->
<div class="doc-header" role="navigation">
<a class="btn pull-right" aria-label="" href="{% url 'pro_list' %}">
<i class="fa fa-home"></i> 返回首页
</a>
<a class="btn pull-left js-toolbar-action" aria-label="" href="javascript:void(0);" title="切换侧边栏">
<i class="fa fa-align-justify"></i>
</a>
<a class="btn pull-left font-small" href="javascript:void(0);" title="缩小字体">
<i class="fa fa-font">-</i>
</a>
<div class="dropdown pull-left font-settings ">
<a class="btn toggle-dropdown font-large" href="javascript:void(0);" title="放大字体">
<i class="fa fa-font">+</i>
</a>
</div>
<a class="btn pull-left font-switch" href="javascript:void(0);" title="切换字体类型">
<i class="fa fa-text-height"></i>
</a>
{% if request.user == doc.create_user %}
<a class="btn pull-left" aria-label="" href="{% url 'modify_doc' doc_id=doc.id %}">
<i class="fa fa-edit"></i> 修改文档
</a>
<a class="btn pull-left" aria-label="" href="{% url 'create_doc' %}" target="_blank">
<i class="fa fa-edit"></i> 新建文档
</a>
{% endif %}
</div>
<!-- 文档主体 -->
<div class="doc-body-content">
<div class="doc-body-content-div">
<!-- 文档内容 -->
<div class="doc-content">
<!-- 标题 -->
<div class="doc-info">
<!-- 文集标题 -->
<h1>搜索{{kw}}共有{{ search_result.count }}个结果 - {{project.name}}</h1>
<hr>
<!-- 广告代码开始 -->
{% if ad_code %}
<div class="ad-code">
{{ ad_code | safe }}
</div>
{% endif %}
<!-- 广告代码结束 -->
</div>
<!-- 标题结束 -->
<!-- 正文开始 -->
<div class="markdown-body" id="content">
{% for result in search_result %}
<div>
<h3><a href="{% url 'doc' pro_id=project.id doc_id=result.id %}" target="_blank">{{ result.name }}</a></h3>
<p>{{ result.pre_content|truncatechars:300 }}</p>
</div>
{% endfor %}
</div>
<!-- 正文结束 -->
</div>
<!-- 文档目录 -->
<div class="doc-cata">
<div id="toc-container"></div>
</div>
<!-- 社交分享 -->
<div class="share-div" style="margin-top: 10px;padding:10px;text-align: center;background-color: #fafafa">
分享到:<span class="social-share"></span>
</div>
</div>
</div>
</div>
<!-- 右侧文档栏结束 -->
<div class="toTop"><i class="layui-icon layui-icon-top" style="font-size: 50px;"></i></div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<script src="{% static 'layui/layui.all.js' %}"></script>
<script src="{% static 'prism/prism.js' %}"></script>
<script src="{% static 'editor.md/lib/marked.min.js' %}"></script>
<script src="{% static 'editor.md/lib/prettify.min.js' %}"></script>
<script src="{% static 'editor.md/lib/raphael.min.js' %}"></script>
<script src="{% static 'editor.md/lib/underscore.min.js' %}"></script>
<script src="{% static 'editor.md/lib/sequence-diagram.min.js' %}"></script>
<script src="{% static 'editor.md/lib/flowchart.min.js' %}"></script>
<script src="{% static 'editor.md/lib/jquery.flowchart.min.js' %}"></script>
<script src="{% static 'editor.md/editormd.min.js' %}"></script>
<script src="{% static 'share.js/js/social-share.min.js' %}"></script>
{% block custom_script %}
<script>
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
//为当前页面的目录链接添加蓝色样式
$("nav li a").each(function (i) {
var $me = $(this);
var lochref = $.trim(window.location.href);
var mehref = $.trim($me.get(0).href);
if (lochref.indexOf(mehref) != -1) {
//console.log($me,lochref,mehref)
$me.closest("li").addClass("active");
//展开当前文档的上级目录
$me.parent("li").parent('ul.sub-menu').toggleClass("toc-close toc-open"); //展开二级目录
$me.parent("div").parent('li').parent('ul.sub-menu').toggleClass("toc-close toc-open"); //展开还有子级的二级目录
$me.parent("li").parent('ul').parent('li').parent('ul.sub-menu').toggleClass("toc-close toc-open"); //展开三级目录
$me.parents("ul.sub-menu").prevAll("div").children("i").toggleClass("fa-chevron-left fa-chevron-down");//切换图标
} else {
//console.log(lochref,mehref)
$me.closest("li").removeClass("active");
}
});
</script>
<!-- 页面初始化字体设置 -->
<script>
font_stauts = window.localStorage.getItem('font-sans')
//font_size = window.localStorage.getItem('font-size')
if(font_stauts == 'serif'){
$(".doc-content").toggleClass("switch-font")
$("#content").toggleClass("switch-font")
}
if(window.localStorage.getItem('font-size')){
font_size = window.localStorage.getItem('font-size')
console.log(font_size)
//$('.doc-info h1').css({'font-size':font_size+'rem'})
$('#content').css({'font-size':font_size+'rem'})
}else{
window.localStorage.setItem('font-size',0.8)
}
</script>
<!-- 返回顶部 -->
<script type="text/javascript">
$(document).ready(function() {
// 初始时,“返回顶部”标签隐藏
$(".toTop").hide();
$(window).scroll(function() {
// 若滚动的高度,超出指定的高度后,“返回顶部”的标签出现。
if($(document).scrollTop() >= 150) {
$(".toTop").show();
} else {
$(".toTop").hide();
}
})
// 绑定点击事件,实现返回顶部的效果
$(".toTop").click(function() {
$(document).scrollTop(0);
});
});
</script>
<!-- 切换隐藏侧边栏 -->
<script>
// 切换侧边栏
$(function(){
$(".js-toolbar-action").click(toggleSidebar);
});
//切换侧边栏显示隐藏
function toggleSidebar(){
console.log("切换侧边栏")
$("body").toggleClass("big-page");
return false;
}
</script>
<!-- 切换内容字体 -->
<script>
//切换文档内容字体类型
$(function(){
$('.font-switch').click(switchFont);
});
function switchFont(){
if(font_stauts == 'serif'){
$(".doc-content").toggleClass("switch-font")
$("#content").toggleClass("switch-font")
window.localStorage.setItem('font-sans','sans')
}else{
$(".doc-content").toggleClass("switch-font")
$("#content").toggleClass("switch-font")
window.localStorage.setItem('font-sans','serif')
}
};
//放大字体
$(function(){
$('.font-large').click(largeFont);
});
function largeFont(){
var font_size = window.localStorage.getItem('font-size')
console.log(font_size)
if(parseFloat(font_size) < 1.4){
size = parseFloat(font_size) + 0.1
//$('.doc-info h1').css({'font-size':size+'rem'})
$('#content').css({'font-size':size+'rem'})
window.localStorage.setItem('font-size',size)
}else{
console.log("xxx")
}
};
//缩小字体
$(function(){
$('.font-small').click(smallFont);
});
function smallFont(){
var font_size = window.localStorage.getItem('font-size')
//console.log(font_size)
if(parseFloat(font_size) >= 0.6){
size = parseFloat(font_size) - 0.2
//$('.doc-info h1').css({'font-size':size+'rem'})
$('#content').css({'font-size':size+'rem'})
window.localStorage.setItem('font-size',size)
}else{
console.log("xxx")
}
};
</script>
<!-- 展开收起左边目录 -->
<script>
$(function(){
$(".switch-toc").click(SwitchToc);
});
function SwitchToc(i){
var $me = $(this);
$(this).parent().next("ul").toggleClass("toc-close"); //切换展开收起样式
$(this).toggleClass("fa-chevron-left fa-chevron-down");//切换图标
};
</script>
<!-- 统计代码开始 -->
{{ static | safe }}
<!-- 统计代码结束 -->
{% endblock %}
</body>
</html>