觅道文档,新增PDF导出下载,优化EPUB导出下载,支持思维导图自定义高度等

This commit is contained in:
yangjian 2020-05-02 18:16:57 +08:00
parent 9bcf043027
commit 7382b8bbbb
30 changed files with 993 additions and 103 deletions

View File

@ -1,5 +1,16 @@
## 版本更新记录
### v.0.5.0 2020-05-02
- MrDoc正式中文取名觅道文档
- 文档编辑器添加Markdown折叠功能
- 思维导图支持图形高度设置;
- 优化文集导出EPUB文件功能
- 新增PDF文件导出功能
- 新增一个广告位;
- 优化文集名称字符验证;
### v0.4.2 2020-04-20
- 添加思维导图功能的支持,可以在文档编辑器通过图标和`mindmap`标识代码块来创建脑图;

View File

@ -1,3 +1,4 @@
# coding:utf-8
"""
Django settings for MrDoc project.
@ -25,7 +26,7 @@ SECRET_KEY = '5&71mt9@^58zdg*_!t(x6g14q*@84d%ptr%%s6e0l50zs0we3d'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
VERSIONS = '0.4.2'
VERSIONS = '0.5.0'
ALLOWED_HOSTS = ['*']

View File

@ -1,4 +1,4 @@
## MrDoc - 记录文档,汇聚思想
## 觅道文档MrDoc - 记录文档,汇聚思想
![Mrdoc首页](./captrue/mrdoc-index.png)
@ -28,13 +28,13 @@
- 支持**思维导图**以Markdown的语法创建思维导图
- 支持流程图、时序图的绘制;
- 两栏式**文档阅读**页面、三级目录层级显示,文档阅读字体缩放,字体类型切换,页面社交分享,移动端阅读优化;
- 支持文集后台**导出打包**`markdown`文本格式`.md`文件、前台导出为`EPUB`等格式文件;
- 支持文集**导出打包**,包括`markdown`文本格式`.md`文件、`EPUB`电子书格式文件和PDF格式文件;
- 基于文集进行**文档权限**控制提供公开、私密、指定用户可见、访问码可见4种权限模式
- 支持基于账户的**`API`接口**,可以借助账户`token`通过`API`获取文集、上传图片和创建文档;
- 支持**文集协作**功能,一个文集可以拥有一个创建者和多个协作者,可灵活选择协作权限;
- 支持**文档历史版本**功能,可以查看和对比历史版本与现有版本的差异,恢复某个历史版本为当前版本;
当前版本为:**v0.4.2**,版本发布时间为**2020-04-20**
当前版本为:**v0.5.0**,版本发布时间为**2020-05-02**
完整更新记录详见:[CHANGES.md](./CHANGES.md)
@ -57,7 +57,10 @@ pip install -r requirements.txt
默认情况下MrDoc使用Django的SQLite数据库如果你使用Sqlite数据库则无需另外配置数据库。
如果有配置其他数据库的需求,请在/MrDoc/MrDoc目录下打开settings.py文件在约80行的位置将如下代码
如果有配置其他数据库的需求请首先按照Django官方的[数据库支持说明](https://docs.djangoproject.com/zh-hans/2.2/ref/databases/)安装特定数据库的Python绑定库
然后在/MrDoc/MrDoc目录下打开settings.py文件在约80行的位置将如下代码
```python
DATABASES = {
'default': {
@ -66,7 +69,9 @@ DATABASES = {
}
}
```
按照自己数据库的信息将其修改如下格式下面以MySQL为例
```python
DATABASES = {
'default': {
@ -79,6 +84,7 @@ DATABASES = {
}
}
```
### 3、初始化数据库
在安装完所需的第三方库并配置好数据库信息之后,我们需要对数据库进行初始化。

View File

@ -12,19 +12,25 @@ import json,datetime,hashlib,random
from app_doc.models import *
from app_admin.models import *
from app_admin.utils import *
import traceback
# 返回验证码图片
def check_code(request):
import io
from . import check_code as CheckCode
stream = io.BytesIO()
# img图片对象,code在图像中写的内容
img, code = CheckCode.create_validate_code()
img.save(stream, "png")
# 图片页面中显示,立即把session中的CheckCode更改为目前的随机字符串值
request.session["CheckCode"] = code
return HttpResponse(stream.getvalue())
try:
import io
from . import check_code as CheckCode
stream = io.BytesIO()
# img图片对象,code在图像中写的内容
img, code = CheckCode.create_validate_code()
img.save(stream, "png")
# 图片页面中显示,立即把session中的CheckCode更改为目前的随机字符串值
request.session["CheckCode"] = code
return HttpResponse(stream.getvalue())
except Exception as e:
if settings.DEBUG:
print(traceback.print_exc())
return HttpResponse("请求异常:{}".format(repr(e)))
# 登录视图
@ -54,6 +60,8 @@ def log_in(request):
errormsg = '用户名或密码错误!'
return render(request, 'login.html', locals())
except Exception as e:
if settings.DEBUG:
print(traceback.print_exc())
return HttpResponse('请求出错')
@ -136,8 +144,8 @@ def log_out(request):
try:
logout(request)
except Exception as e:
print(e)
# logger.error(e)
if settings.DEBUG:
print(traceback.print_exc())
return redirect(request.META['HTTP_REFERER'])
@ -165,7 +173,8 @@ def forget_pwd(request):
errormsg = "验证码已过期"
return render(request, 'forget_pwd.html', locals())
except Exception as e:
print(repr(e))
if settings.DEBUG:
print(traceback.print_exc())
errormsg = "验证码错误"
return render(request,'forget_pwd.html',locals())
@ -514,7 +523,8 @@ def admin_setting(request):
if types == 'basic':
close_register = request.POST.get('close_register',None) # 禁止注册
static_code = request.POST.get('static_code',None) # 统计代码
ad_code = request.POST.get('ad_code',None) # 广告代码
ad_code = request.POST.get('ad_code',None) # 广告位1
ad_code_2 = request.POST.get('ad_code_2',None) # 广告位2
beian_code = request.POST.get('beian_code',None) # 备案号
enbale_email = request.POST.get("enable_email",None) # 启用邮箱
enable_register_code = request.POST.get('enable_register_code',None) # 注册邀请码
@ -534,6 +544,10 @@ def admin_setting(request):
name = 'ad_code',
defaults={'value':ad_code,'types':'basic'}
)
SysSetting.objects.update_or_create(
name='ad_code_2',
defaults={'value': ad_code_2, 'types': 'basic'}
)
# 更新备案号
SysSetting.objects.update_or_create(
name='beian_code',

View File

@ -0,0 +1,29 @@
# Generated by Django 2.2.12 on 2020-04-26 11:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('app_doc', '0019_dochistory_create_user'),
]
operations = [
migrations.CreateModel(
name='ProjectReportFile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('file_type', models.CharField(choices=[('epub', 'epub'), ('pdf', 'pdf'), ('docx', 'docx')], max_length=10, verbose_name='文件类型')),
('file_name', models.CharField(max_length=100, verbose_name='文件名称')),
('file_path', models.CharField(max_length=250, verbose_name='文件路径')),
('create_time', models.DateTimeField(auto_now_add=True)),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app_doc.Project')),
],
options={
'verbose_name': '附件管理',
'verbose_name_plural': '附件管理',
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.12 on 2020-05-01 10:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app_doc', '0020_projectreportfile'),
]
operations = [
migrations.AddField(
model_name='projectreport',
name='allow_pdf',
field=models.IntegerField(default=0, verbose_name='前台导出PDF'),
),
]

View File

@ -106,6 +106,7 @@ class ProjectReport(models.Model):
project = models.OneToOneField(Project,unique=True,on_delete=models.CASCADE)
# 允许导出默认为0-允许1-不允许
allow_epub = models.IntegerField(default=0,verbose_name="前台导出EPUB")
allow_pdf = models.IntegerField(default=0, verbose_name="前台导出PDF")
def __str__(self):
return self.project.name
@ -114,6 +115,22 @@ class ProjectReport(models.Model):
verbose_name = '文集导出'
verbose_name_plural = verbose_name
# 文集导出文集模型
class ProjectReportFile(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE) # 外键关联文集
file_type = models.CharField(choices=(('epub', 'epub'), ('pdf', 'pdf'), ('docx', 'docx')), verbose_name='文件类型',max_length=10)
file_name = models.CharField(max_length=100, verbose_name='文件名称')
file_path = models.CharField(max_length=250, verbose_name='文件路径')
create_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.file_name
class Meta:
verbose_name = '附件管理'
verbose_name_plural = verbose_name
# 图片分组模型
class ImageGroup(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE)

View File

@ -21,6 +21,125 @@ django.setup()
from app_doc.models import *
import traceback
import time
from pyppeteer import launch
import asyncio
# import PyPDF2
# from pdfminer import high_level
# 动态脑图转静态图片
def genera_mindmap_img(html_path,img_path):
async def main():
browser = await launch(headless=True,handleSIGINT=False,handleSIGTERM=False,handleSIGHUP=False)
page = await browser.newPage()
await page.goto('file://' + html_path, {'waitUntil': 'networkidle0'})
element = await page.querySelector('.mindmap')
await element.screenshot({'type': 'jpeg', 'quality': 100, 'path': img_path})
await browser.close()
# asyncio.new_event_loop().run_until_complete(main())
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
# 公式转图片
def genera_tex_img(html_path,img_path):
async def main():
browser = await launch(headless=True,handleSIGINT=False,handleSIGTERM=False,handleSIGHUP=False)
page = await browser.newPage()
await page.goto('file://' + html_path, {'waitUntil': 'networkidle0'})
element = await page.querySelector('.editormd-tex')
await element.screenshot({'type': 'jpeg', 'quality': 100, 'path': img_path})
await browser.close()
# asyncio.new_event_loop().run_until_complete(main())
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
# 流程图转图片
def genera_flowchart_img(html_path,img_path):
async def main():
browser = await launch(headless=True,handleSIGINT=False,handleSIGTERM=False,handleSIGHUP=False)
page = await browser.newPage()
await page.goto('file://' + html_path, {'waitUntil': 'networkidle0'})
element = await page.querySelector('.flowchart')
await element.screenshot({'type': 'jpeg', 'quality': 100, 'path': img_path})
await browser.close()
# asyncio.new_event_loop().run_until_complete(main())
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
# 时序图转图片
def genera_seque_img(html_path,img_path):
async def main():
browser = await launch(headless=True,handleSIGINT=False,handleSIGTERM=False,handleSIGHUP=False)
page = await browser.newPage()
await page.goto('file://' + html_path, {'waitUntil': 'networkidle0'})
element = await page.querySelector('.sequence-diagram')
await element.screenshot({'type': 'jpeg', 'quality': 100, 'path': img_path})
await browser.close()
# asyncio.new_event_loop().run_until_complete(main())
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
# HTML转PDF
def html_to_pdf(html_path,pdf_path):
async def main():
browser = await launch(
headless=True,
handleSIGINT=False,
handleSIGTERM=False,
handleSIGHUP=False,
ignoreHTTPSErrors = True,
)
page = await browser.newPage()
await page.goto('file://' + html_path, {'waitUntil': 'networkidle0'})
await page.pdf({
'path':pdf_path,
'format':'A4',
'displayHeaderFooter':True,
'headerTemplate':'<div></div>',
'footerTemplate':'<div style="text-align:center;width:297mm;font-size: 8px;"><span class="pageNumber"></span>/<span class="totalPages"></span></div>',
'margin':{
'top':'1cm',
'right':'1cm',
'bottom':'1cm',
'left':'1cm'
}
})
await browser.close()
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main())
except:
loop.run_until_complete(main())
finally:
loop.close()
# 导出MD文件压缩包
class ReportMD():
@ -55,7 +174,7 @@ class ReportMD():
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
@ -70,7 +189,6 @@ class ReportMD():
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文件
@ -138,7 +256,7 @@ class ReportMD():
class ReportEPUB():
def __init__(self,project_id):
self.project = Project.objects.get(id=project_id)
self.base_path = settings.MEDIA_ROOT + '/report/{}/'.format(project_id)
self.base_path = settings.MEDIA_ROOT + '/report_epub/{}/'.format(project_id)
# 创建相关目录
if os.path.exists(self.base_path + '/OEBPS') is False:
@ -155,7 +273,7 @@ class ReportEPUB():
# 复制样式文件到相关目录
shutil.copyfile(settings.BASE_DIR+'/static/report_epub/style.css',self.base_path + '/OEBPS/Styles/style.css')
shutil.copyfile(settings.BASE_DIR+'/static/katex/katex.min.css',self.base_path + '/OEBPS/Styles/katex.css')
shutil.copyfile(settings.BASE_DIR+'/static/editor.md/css/editormd.min.css/',self.base_path + '/OEBPS/Styles/editormd.css')
shutil.copyfile(settings.BASE_DIR+'/static/editor.md/css/editormd.min.css',self.base_path + '/OEBPS/Styles/editormd.css')
# 复制封面图片到相关目录
shutil.copyfile(settings.BASE_DIR+'/static/report_epub/epub_cover1.jpg',self.base_path + '/OEBPS/Images/epub_cover1.jpg')
@ -164,8 +282,11 @@ class ReportEPUB():
# 使用BeautifulSoup解析拼接好的HTML文本
html_soup = BeautifulSoup(html_str, 'lxml')
src_tag = html_soup.find_all(lambda tag: tag.has_attr("src")) # 查找所有包含src的标签
code_tag = html_soup.find_all(name="code")
# print(src_tag)
mindmap_tag = html_soup.select('svg.mindmap') # 查找所有脑图的SVG标签
tex_tag = html_soup.select('.editormd-tex') # 查找所有公式标签
flowchart_tag = html_soup.select('.flowchart') # 查找所有流程图标签
seque_tag = html_soup.select('.sequence-diagram') # 查找所有时序图标签
code_tag = html_soup.find_all(name="code") # 查找code代码标签
# 添加css样式标签
style_link = html_soup.new_tag(name='link',href="../Styles/style.css",rel="stylesheet",type="text/css")
@ -177,6 +298,7 @@ class ReportEPUB():
# 添加xlm标签声明
# html_soup.html.insert_before('<?xml version="1.0" encoding="UTF-8"?>')
# 添加html标签的xmlns属性
html_soup.html['xmlns'] = "http://www.w3.org/1999/xhtml"
@ -195,6 +317,160 @@ class ReportEPUB():
except FileNotFoundError as e:
pass
# 替换HTML文本中的脑图为静态图片
for mindmap in mindmap_tag:
print('转换脑图')
html_str = '''<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Markmap</title>
<script src="../../static/jquery/3.1.1/jquery.min.js"></script>
<script src="../../static/mindmap/d3@5.js"></script>
<script src="../../static/mindmap/transform.js"></script>
<script src="../../static/mindmap/view.js"></script>
</head>
<body>
{svg_content}
<script>
var mmap = $('svg.mindmap');
var md_data = window.markmap.transform(mmap.text().trim());
window.markmap.markmap("svg.mindmap",md_data)
</script>
</body>
</html>
'''.format(svg_content=mindmap)
# 脑图HTML文件路径
temp_mindmap_html = settings.BASE_DIR +'/media/report_epub/mindmap_{}.html'.format(str(time.time()))
mindmap_img_filename = 'mindmap_{}.jpg'.format(str(time.time()))
mindmap_img_path = self.base_path + '/OEBPS/Images/' + mindmap_img_filename
with open(temp_mindmap_html,'w+',encoding='utf-8') as mindmap_html:
mindmap_html.write(html_str)
genera_mindmap_img(temp_mindmap_html,mindmap_img_path)
# 将图片标签设置进去
mindmap.name = 'img'
mindmap['src'] = '../Images/' + mindmap_img_filename
mindmap.string = ''
os.remove(temp_mindmap_html) # 删除临时的HTML
# 替换公式为静态图片
for tex in tex_tag:
print('转换公式')
html_str = '''<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="../../static/katex/katex.min.css" />
<title>Markmap</title>
<script src="../../static/jquery/3.1.1/jquery.min.js"></script>
<script src="../../static/editor.md/editormd.js"></script>
<script src="../../static/katex/katex.min.js"></script>
</head>
<body>
{content}
</body>
<script>
var tex = $('.editormd-tex');
katex.render(tex.html().replace(/&lt;/g, "<").replace(/&gt;/g, ">"), tex[0]);
tex.find(".katex").css("font-size", "1.6em");
</script>
</body>
</html>
'''.format(content=tex)
# 公式HTML文件路径
temp_tex_html = settings.BASE_DIR + '/media/report_epub/tex_{}.html'.format(str(time.time()))
tex_img_filename = 'tex_{}.jpg'.format(str(time.time()))
tex_img_path = self.base_path + '/OEBPS/Images/' + tex_img_filename
with open(temp_tex_html, 'w+', encoding='utf-8') as tex_html:
tex_html.write(html_str)
genera_tex_img(temp_tex_html, tex_img_path)
# 将图片标签添加进去
# tex.name = 'img'
# tex['src'] = '../Images/' + tex_img_filename
tex.string = ''
tex_img_tag = html_soup.new_tag(name='img',src='../Images/' + tex_img_filename)
tex.insert(0,tex_img_tag)
os.remove(temp_tex_html) # 删除临时的HTML
# 替换流程图为静态图片
for flowchart in flowchart_tag:
print("转换流程图")
html_str = '''<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="../../static/katex/katex.min.css" />
<title>Markmap</title>
<script src="../../static/jquery/3.1.1/jquery.min.js"></script>
<script src="../../static/editor.md/lib/raphael.min.js"></script>
<script src="../../static/editor.md/lib/flowchart.min.js"></script>
<script src="../../static/editor.md/lib/jquery.flowchart.min.js"></script>
</head>
<body>
{content}
</body>
<script>
$(".flowchart").flowChart();
</script>
</body>
</html>
'''.format(content=flowchart)
# 流程图HTML文件路径
temp_flow_html = settings.BASE_DIR + '/media/report_epub/flow_{}.html'.format(str(time.time()))
flow_img_filename = 'flow_{}.jpg'.format(str(time.time()))
flow_img_path = self.base_path + '/OEBPS/Images/' + flow_img_filename
with open(temp_flow_html, 'w+', encoding='utf-8') as flow_html:
flow_html.write(html_str)
genera_flowchart_img(temp_flow_html, flow_img_path)
# 将图片标签添加进去
flowchart.string = ''
flow_img_tag = html_soup.new_tag(name='img', src='../Images/' + flow_img_filename)
flowchart.insert(0, flow_img_tag)
os.remove(temp_flow_html) # 删除临时的HTML
# 替换时序图为静态图片
for seque in seque_tag:
print("转换时序图")
html_str = '''<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Markmap</title>
<script src="../../static/jquery/3.1.1/jquery.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>
</head>
<body>
{content}
</body>
<script>
$(".sequence-diagram").sequenceDiagram({{theme: "simple"}});
</script>
</body>
</html>
'''.format(content=seque)
# 时序图HTML文件路径
temp_seque_html = settings.BASE_DIR + '/media/report_epub/seque_{}.html'.format(str(time.time()))
seque_img_filename = 'seque_{}.jpg'.format(str(time.time()))
seque_img_path = self.base_path + '/OEBPS/Images/' + seque_img_filename
with open(temp_seque_html, 'w+', encoding='utf-8') as seque_html:
seque_html.write(html_str)
genera_seque_img(temp_seque_html, seque_img_path)
# 将图片标签添加进去
seque.string = ''
seque_img_tag = html_soup.new_tag(name='img', src='../Images/' + seque_img_filename)
seque.insert(0, seque_img_tag)
os.remove(temp_seque_html) # 删除临时的HTML
# 替换code标签的内容
# for code in code_tag:
# code_str = code.get_text()
@ -521,11 +797,11 @@ class ReportEPUB():
def generate_epub(self):
try:
# 生成ZIP压缩文件
zipfile_name = settings.MEDIA_ROOT + '/report/{}'.format(self.project.name)+'_'+str(int(time.time()))
zipfile_name = settings.MEDIA_ROOT + '/report_epub/{}'.format(self.project.name)+'_'+str(int(time.time()))
zip_name = shutil.make_archive(
base_name = zipfile_name,
format='zip',
root_dir= settings.MEDIA_ROOT + '/report/{}'.format(self.project.id)
root_dir= settings.MEDIA_ROOT + '/report_epub/{}'.format(self.project.id)
)
# print(zip_name)
# 修改zip压缩文件后缀为EPUB
@ -550,6 +826,186 @@ class ReportEPUB():
epub_file = self.generate_epub()
return epub_file
# 导出PDF
class ReportPDF():
def __init__(self,project_id):
# 查询文集信息
self.pro_id = project_id
self.html_str = '''
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{title}</title>
<link rel="stylesheet" href="../../static/editor.md/css/editormd.css" />
<link rel="stylesheet" href="../../static/katex/katex.min.css" />
<script src="../../static/jquery/3.1.1/jquery.min.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/mindmap/d3@5.js"></script>
<script src="../../static/mindmap/transform.js"></script>
<script src="../../static/mindmap/view.js"></script>
<script src="../../static/katex/katex.min.js"></script>
<script src="../../static/editor.md/editormd.js"></script>
</head>
<body>
<div style="position: fixed;font-size:8px; bottom: 5px; right: 10px; background: red; z-index: 10000">
本文档由觅道文档(MrDoc)生成
</div>
<div style="text-align:center;margin-top:400px;">
<h1>{project_name}</h1>
<p>作者{author}</p>
<p>日期{create_time}</p>
</div>\n
<div class="markdown-body" id="content" style="padding:0px;font-family:宋体;">
<textarea style="display: none;">{pre_content}</textarea>
</div>
<script>
editormd.markdownToHTML("content", {{
htmlDecode : "style,script,iframe",
emoji : true, //emoji表情
taskList : true, // 任务列表
tex : true, // 科学公式
flowChart : true, // 流程图
sequenceDiagram : true, // 时序图
tocm : true, //目录
toc :true,
tocContainer : "#toc-container",
tocDropdown : false,
atLink : false,//禁用@链接
}});
$('html').find(".editormd-tex").each(function(){{
var tex = $(this);
katex.render(tex.html().replace(/&lt;/g, "<").replace(/&gt;/g, ">"), tex[0]);
tex.find(".katex").css("font-size", "1.6em");
}});
$('img.emoji').each(function(){{
var img = $(this);
if(img[0].src.indexOf("/static/editor.md/")){{
var src = img[0].src.split('static');
img[0].src = '../../static' + src[1];
}}
}})
</script>
</body>
</html>
'''
self.content_str = ""
def work(self):
try:
project = Project.objects.get(pk=self.pro_id)
except:
return
# 拼接文档的HTML字符串
data = Doc.objects.filter(top_doc=self.pro_id,parent_doc=0).order_by("sort")
toc_list = {'1':[],'2':[],'3':[]}
for d in data:
self.content_str += "<h1 style='page-break-before: always;'>{}</h1>\n\n".format(d.name)
self.content_str += d.pre_content + '\n'
toc_list['1'].append({'id':d.id,'name':d.name})
# 获取第二级文档
data_2 = Doc.objects.filter(parent_doc=d.id).order_by("sort")
for d2 in data_2:
self.content_str += "\n\n<h1 style='page-break-before: always;'>{}</h1>\n\n".format(d2.name)
self.content_str += d2.pre_content + '\n'
toc_list['2'].append({'id':d2.id,'name':d2.name,'parent':d.id})
# 获取第三级文档
data_3 = Doc.objects.filter(parent_doc=d2.id).order_by("sort")
for d3 in data_3:
# print(d3.name,d3.content)
self.content_str += "\n\n<h1 style='page-break-before: always;'>{}</h1>\n\n".format(d3.name)
self.content_str += d3.pre_content +'\n'
toc_list['3'].append({'id':d3.id,'name':d3.name,'parent':d2.id})
# 替换所有媒体文件链接
self.content_str = self.content_str.replace('![](/media//','![](../../media/')
# print(self.html_str.format(pre_content=self.content_str))
# 创建写入临时HTML文件
report_pdf_folder = settings.MEDIA_ROOT+'/report_pdf'
is_folder = os.path.exists(report_pdf_folder)
# 创建文件夹
if is_folder is False:
os.mkdir(report_pdf_folder)
# 临时HTML和PDF文件名
temp_file_name = '{}_{}'.format(
project.name,
str(datetime.datetime.today()).replace(' ', '-').replace(':', '-')
)
# 临时HTML文件路径
temp_file_path = report_pdf_folder + '/{0}.html'.format(temp_file_name)
# PDF文件路径
report_file_path = report_pdf_folder + '/{0}.pdf'.format(temp_file_name)
# output_pdf_path = report_pdf_folder + '/{}_{}.pdf'.format(
# project.name,
# str(datetime.datetime.today()).replace(' ','-').replace(':','-')
# )
# 写入HTML文件
with open(temp_file_path, 'w', encoding='utf-8') as htmlfile:
htmlfile.write(
self.html_str.format(
title=project.name,
pre_content=self.content_str,
project_name=project.name,
author=project.create_user,
create_time=str(datetime.date.today())
)
)
# 执行HTML转PDF
html_to_pdf(temp_file_path,report_file_path)
# 处理PDF文件
if os.path.exists(report_file_path):
# output = PyPDF2.PdfFileWriter() # 实例化一个PDF写入文件类用于保存最后的PDF文件
# tmp_pdf_file = open(report_file_path, 'rb') # 打开临时PDF
# input = PyPDF2.PdfFileReader(tmp_pdf_file) # 打开临时PDF文件
# pdf_pages = input.getNumPages() # 获取临时PDF的页数
# for p in range(pdf_pages):
# page = input.getPage(p)
# output.addPage(page) # 添加一页
# page_content = high_level.extract_text(report_file_path, page_numbers=[p]) # 提取某页的文本
# first_line_text = page_content.split('\n') # 获取某页的第一行文本
# # 添加第一层级文档书签
# for i1 in toc_list['1']:
# if i1['name'] in first_line_text:
# bookmark_1 = output.addBookmark(i1['name'], p, parent=None) # 添加书签
# else:
# bookmark_1 = None
# # 添加第二层文档书签
# for i2 in toc_list['2']:
# if i2['name'] in first_line_text:
# bookmark_2 = output.addBookmark(i2['name'], p, parent=bookmark_1) # 添加书签
# # 添加第三层文档书签
# for i3 in toc_list['3']:
# if i3['name'] in first_line_text:
# bookmark_3 = output.addBookmark(i3['name'], p, parent=bookmark_2) # 添加书签
#
# output.setPageMode("/UseOutlines") # 默认打开书签
# with open(output_pdf_path, 'wb') as output_pdf_file:
# output.write(output_pdf_file)
# output_pdf_file.close()
# 删除临时HTML文件和临时PDF文件
# tmp_pdf_file.close() # 关闭临时PDF文件
os.remove(temp_file_path)
# os.remove(report_file_path)
# print(report_file_path)
return report_file_path
else:
return False
# 导出Docx
class ReportDocx():
def __init__(self,project_id):
@ -697,7 +1153,12 @@ if __name__ == '__main__':
# project_id=7
# )
# app.work()
app = ReportEPUB(project_id=20)
# app = ReportEPUB(project_id=20)
# app.work()
app = ReportPDF(project_id=20)
app.work()
# app = ReportDocx(project_id=20)
# app.work()

View File

@ -19,9 +19,9 @@ def get_new_doc(value):
new_doc = '它还没有文档……'
return new_doc
# 获取文集的开放导出状态
@register.filter(name='get_report_status')
def get_report_status(value):
# 获取文集的EPUB开放导出状态
@register.filter(name='report_status_epub')
def get_report_status_epub(value):
try:
project = Project.objects.get(id=int(value))
status = ProjectReport.objects.get(project=project).allow_epub
@ -30,6 +30,17 @@ def get_report_status(value):
status = 0
return status
# 获取文集的PDF开放导出状态
@register.filter(name='report_status_pdf')
def get_report_status_pdf(value):
try:
project = Project.objects.get(id=int(value))
status = ProjectReport.objects.get(project=project).allow_pdf
except Exception as e:
# print(repr(e))
status = 0
return status
# 获取图片分组的图片数量
@register.filter(name='img_group_cnt')
def get_img_group_cnt(value):

View File

@ -12,6 +12,7 @@ urlpatterns = [
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('genera_project_file/',views.genera_project_file,name='genera_project_file'), # 个人中心生成文集文件epub\docx\pdf等
path('report_project_file/',views.report_file,name='report_file'), # 导出文集文件(epub、docx等)
path('modify_pro_role/<int:pro_id>/',views.modify_project_role,name="modify_pro_role"),# 修改文集权限
path('modify_pro_download/<int:pro_id>/', views.modify_project_download, name="modify_pro_download"), # 修改文集前台下载权限

View File

@ -9,6 +9,7 @@ from django.core.exceptions import PermissionDenied,ObjectDoesNotExist
from app_doc.models import Project,Doc,DocTemp
from django.contrib.auth.models import User
from django.db.models import Q
from django.db import transaction
import datetime
import traceback
import re
@ -18,7 +19,7 @@ import os.path
# 替换前端传来的非法字符
def validateTitle(title):
rstr = r"[\/\\\:\*\?\"\<\>\|]" # '/ \ : * ? " < > |'
rstr = r"[\/\\\:\*\?\"\<\>\|\[\]]" # '/ \ : * ? " < > |'
new_title = re.sub(rstr, "_", title) # 替换为下划线
return new_title
@ -181,7 +182,7 @@ def create_project(request):
role_list = ['0','1','2','3',0,1,2,3]
if name != '':
project = Project.objects.create(
name=name,
name=validateTitle(name),
intro=desc[:100],
create_user=request.user,
role = int(role) if role in role_list else 0
@ -214,9 +215,9 @@ def project_index(request,pro_id):
# 获取问价文集前台下载权限
try:
allow_epub_download = ProjectReport.objects.get(project=project).allow_epub
allow_download = ProjectReport.objects.get(project=project)
except ObjectDoesNotExist:
allow_epub_download = 0
allow_download = False
# 私密文集并且访问者非创建者非协作者
if (project.role == 1) and (request.user != project.create_user) and (colla_user == 0):
@ -266,7 +267,7 @@ def modify_project(request):
if (request.user == project.create_user) or request.user.is_superuser:
name = request.POST.get('name',None)
content = request.POST.get('desc',None)
project.name = name
project.name = validateTitle(name)
project.intro = content
project.save()
return JsonResponse({'status':True,'data':'修改成功'})
@ -415,18 +416,30 @@ def modify_project_download(request,pro_id):
if (pro.create_user != request.user) and (request.user.is_superuser is False):
return render(request,'403.html')
else:
project_files = ProjectReportFile.objects.filter(project=pro)
if request.method == 'GET':
return render(request,'app_doc/manage_project_download.html',locals())
elif request.method == 'POST':
download_epub = request.POST.get('download_epub',None)
download_pdf = request.POST.get('download_pdf', None)
# print("epub状态:",download_epub)
# EPUB下载权限
if download_epub == 'on':
epub_status = 1
else:
epub_status = 0
# PDF下载权限
if download_pdf == 'on':
pdf_status = 1
else:
pdf_status = 0
# 写入数据库
ProjectReport.objects.update_or_create(
project = pro,defaults={'allow_epub':epub_status}
)
ProjectReport.objects.update_or_create(
project=pro, defaults={'allow_pdf': pdf_status}
)
return render(request,'app_doc/manage_project_download.html',locals())
@ -526,7 +539,6 @@ def doc(request,pro_id,doc_id):
# 私密文集且访问者非创建者、协作者 - 不能访问
if (project.role == 1) and (request.user != project.create_user) and (colla_user == 0):
return render(request, '404.html')
# 指定用户可见文集
elif project.role == 2:
user_list = project.role_value
@ -842,6 +854,7 @@ def diff_doc(request,doc_id,his_id):
print(traceback.print_exc())
return JsonResponse({'status':False,'data':'获取异常'})
# 管理文档历史版本
@login_required()
def manage_doc_history(request,doc_id):
@ -1106,7 +1119,6 @@ def get_pro_doc_tree(request):
def handle_404(request):
return render(request,'404.html')
# 导出文集MD文件
@login_required()
def report_md(request):
@ -1133,43 +1145,252 @@ def report_md(request):
else:
return Http404
# 导出文集文件
# 生成文集文件 - 个人中心 - 文集管理
@login_required()
def genera_project_file(request):
if request.method == 'POST':
report_type = request.POST.get('types',None) # 获取前端传入到导出文件类型参数
# 导出EPUB文件
pro_id = request.POST.get('pro_id')
try:
project = Project.objects.get(id=int(pro_id))
# 获取文集的协作用户信息
if request.user.is_authenticated:
colla_user = ProjectCollaborator.objects.filter(project=project, user=request.user)
if colla_user.exists():
colla_user_role = colla_user[0].role
colla_user = colla_user.count()
else:
colla_user = colla_user.count()
else:
colla_user = 0
# 公开的文集 - 可以直接导出
if project.role == 0:
allow_export = True
# 私密文集 - 非创建者和协作者不可导出
elif (project.role == 1):
if (request.user != project.create_user) and (colla_user == 0):
allow_export = False
else:
allow_export = True
# 指定用户可见文集 - 指定用户、文集创建者和协作者可导出
elif project.role == 2:
user_list = project.role_value
if request.user.is_authenticated: # 认证用户判断是否在许可用户列表中
if (request.user.username not in user_list) and \
(request.user != project.create_user) and \
(colla_user == 0): # 访问者不在指定用户之中,也不是协作者
allow_export = False
else:
allow_export = True
else: # 游客直接返回404
allow_export = False
# 访问码可见文集 - 文集创建者、协作者和通过验证即可导出
elif project.role == 3:
# 浏览用户不为创建者和协作者 - 需要访问码
if (request.user != project.create_user) and (colla_user == 0):
viewcode = project.role_value
viewcode_name = 'viewcode-{}'.format(project.id)
r_viewcode = request.COOKIES[
viewcode_name] if viewcode_name in request.COOKIES.keys() else 0 # 从cookie中获取访问码
if viewcode != r_viewcode: # cookie中的访问码不等于文集访问码不可导出
allow_export = False
else:
allow_export = True
else:
allow_export = True
else:
allow_export = False
# 允许被导出
if allow_export:
# 导出EPUB
if report_type in ['epub']:
try:
report_project = ReportEPUB(
project_id=project.id
).work()
# print(report_project)
report_file_path = report_project.split('media', maxsplit=1)[-1] # 导出文件的路径
epub_file = '/media' + report_file_path + '.epub' # 文件相对路径
# 查询文集是否存在导出文件
report_cnt = ProjectReportFile.objects.filter(project=project,file_type='epub')
# 存在文件删除
if report_cnt.count() != 0:
for r in report_cnt:
is_exist = os.path.exists(settings.BASE_DIR + r.file_path)
if is_exist:
os.remove(settings.BASE_DIR + r.file_path)
report_cnt.delete() # 删除数据库记录
# 创建数据库记录
ProjectReportFile.objects.create(
project=project,
file_type='epub',
file_name=epub_file,
file_path=epub_file
)
return JsonResponse({'status': True, 'data': epub_file})
except Exception as e:
if settings.DEBUG:
print(traceback.print_exc())
return JsonResponse({'status': False, 'data': '生成出错'})
# 导出PDF
elif report_type in ['pdf']:
try:
report_project = ReportPDF(
project_id=project.id
).work()
if report_project is False:
return JsonResponse({'status':False,'data':'生成出错'})
report_file_path = report_project.split('media', maxsplit=1)[-1] # 导出文件的路径
pdf_file = '/media' + report_file_path # 文件相对路径
# 查询文集是否存在导出文件
report_cnt = ProjectReportFile.objects.filter(project=project, file_type='pdf')
# 存在文件删除
if report_cnt.count() != 0:
for r in report_cnt:
is_exist = os.path.exists(settings.BASE_DIR + r.file_path)
if is_exist:
os.remove(settings.BASE_DIR + r.file_path)
report_cnt.delete() # 删除数据库记录
# 创建数据库记录
ProjectReportFile.objects.create(
project=project,
file_type='pdf',
file_name=pdf_file,
file_path=pdf_file
)
return JsonResponse({'status': True, 'data': pdf_file})
except Exception as e:
if settings.DEBUG:
print(traceback.print_exc())
return JsonResponse({'status': False, 'data': '生成出错'})
else:
return JsonResponse({'status': False, 'data': '不支持的类型'})
# 不允许被导出
else:
return JsonResponse({'status':False,'data':'无权限导出'})
except ObjectDoesNotExist:
return JsonResponse({'status':False,'data':'文集不存在'})
except Exception as e:
if settings.DEBUG:
print(traceback.print_exc())
return JsonResponse({'status':False,'data':'系统异常'})
else:
return Http404
# 获取文集前台导出文件
@allow_report_file
def report_file(request):
if request.method == 'POST':
report_type = request.POST.get('types',None)
if report_type in ['epub']:
pro_id = request.POST.get('pro_id')
try:
project = Project.objects.get(id=int(pro_id))
# 公开的文集 - 可以直接导出
if project.role == 0:
report_project = ReportEPUB(
project_id=project.id
).work()
# print(report_project)
report_file_path = report_project.split('media',maxsplit=1)[-1]
epub_file = '/media' + report_file_path + '.epub'
return JsonResponse({'status':True,'data':epub_file})
# 私密文集 - 拥有者可导出
elif project.role == 1:
pass
# 指定用户可见文集 - 指定用户可导出
elif project.role == 2:
pass
# 访问码可见文集 - 通过验证即可导出
elif project.role == 3:
pass
report_type = request.POST.get('types',None) # 获取前端传入到导出文件类型参数
pro_id = request.POST.get('pro_id')
try:
project = Project.objects.get(id=int(pro_id))
# 获取文集的协作用户信息
if request.user.is_authenticated:
colla_user = ProjectCollaborator.objects.filter(project=project, user=request.user)
if colla_user.exists():
colla_user_role = colla_user[0].role
colla_user = colla_user.count()
else:
return JsonResponse({'status':False,'data':'不存在的文集权限'})
except ObjectDoesNotExist:
return JsonResponse({'status':False,'data':'文集不存在'})
except Exception as e:
if settings.DEBUG:
print(traceback.print_exc())
return JsonResponse({'status':False,'data':'系统异常'})
else:
return JsonResponse({'status':False,'data':'不支持的类型'})
colla_user = colla_user.count()
else:
colla_user = 0
# 公开的文集 - 可以直接导出
if project.role == 0:
allow_export = True
# 私密文集 - 非创建者和协作者不可导出
elif (project.role == 1):
if (request.user != project.create_user) and (colla_user == 0):
allow_export = False
else:
allow_export = True
# 指定用户可见文集 - 指定用户、文集创建者和协作者可导出
elif project.role == 2:
user_list = project.role_value
if request.user.is_authenticated: # 认证用户判断是否在许可用户列表中
if (request.user.username not in user_list) and \
(request.user != project.create_user) and \
(colla_user == 0): # 访问者不在指定用户之中,也不是协作者
allow_export = False
else:
allow_export = True
else: # 游客直接返回404
allow_export = False
# 访问码可见文集 - 文集创建者、协作者和通过验证即可导出
elif project.role == 3:
# 浏览用户不为创建者和协作者 - 需要访问码
if (request.user != project.create_user) and (colla_user == 0):
viewcode = project.role_value
viewcode_name = 'viewcode-{}'.format(project.id)
r_viewcode = request.COOKIES[
viewcode_name] if viewcode_name in request.COOKIES.keys() else 0 # 从cookie中获取访问码
if viewcode != r_viewcode: # cookie中的访问码不等于文集访问码不可导出
allow_export = False
else:
allow_export = True
else:
allow_export = True
else:
allow_export = False
# return JsonResponse({'status':False,'data':'不存在的文集权限'})
if allow_export:
# 导出EPUB文件
if report_type in ['epub']:
try:
try:
report_project = ProjectReportFile.objects.get(project=project,file_type='epub')
except ObjectDoesNotExist:
return JsonResponse({'status':False,'data':'无可用文件,请联系文集创建者'})
# print(report_project)
return JsonResponse({'status': True, 'data': report_project.file_path})
except Exception as e:
if settings.DEBUG:
print(traceback.print_exc())
return JsonResponse({'status': False, 'data': '导出出错'})
# 导出PDF
elif report_type in ['pdf']:
try:
try:
report_project = ProjectReportFile.objects.get(project=project,file_type='pdf')
except ObjectDoesNotExist:
return JsonResponse({'status':False,'data':'无可用文件,请联系文集创建者'})
# print(report_project)
return JsonResponse({'status': True, 'data': report_project.file_path})
except Exception as e:
if settings.DEBUG:
print(traceback.print_exc())
return JsonResponse({'status': False, 'data': '导出出错'})
else:
return JsonResponse({'status': False, 'data': '不支持的类型'})
else:
return JsonResponse({'status':False,'data':'无权限导出'})
except ObjectDoesNotExist:
return JsonResponse({'status':False,'data':'文集不存在'})
except Exception as e:
if settings.DEBUG:
print(traceback.print_exc())
return JsonResponse({'status':False,'data':'系统异常'})
else:
return Http404

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 181 KiB

View File

@ -1,5 +1,5 @@
beautifulsoup4==4.8.2
django==2.2.12
beautifulsoup4==4.8.2
lxml
pillow==6.2.2
pytz==2019.1
sqlparse==0.3.0
pyppeteer==0.0.25

View File

@ -3672,7 +3672,15 @@
}
// var map_id = lang.split('>')[1];
// console.log(map_id)
return "<svg class='mindmap' style='width:100%;min-height=150px;' id='mindmap-"+ map_id +"'>"+code+"</svg>";
var custom_height;
var h = lang.split('>')[1];
if(h != undefined){
custom_height = h;
}else{
custom_height = 150;
}
return "<svg class='mindmap' style='width:100%;min-height:150px;height:"+ custom_height +"px;' id='mindmap-"+ map_id +"'>"+code+"</svg>";
}
else
{
@ -3952,7 +3960,7 @@
taskList : false, // Github Flavored Markdown task lists
emoji : false,
flowChart : false,
mindMap : true, //百度脑图
mindMap : true, //脑图
sequenceDiagram : false,
previewCodeHighlight : true
};

BIN
static/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -3,7 +3,7 @@
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>拒绝访问 - MrDoc</title>
<title>拒绝访问 - 觅道文档MrDoc</title>
<link href="{% static 'layui/css/layui.css' %}" rel="stylesheet">
<link href="{% static 'mrdoc.css' %}" rel="stylesheet">
</head>

View File

@ -3,7 +3,7 @@
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>404 页面未找到 - MrDoc</title>
<title>404 页面未找到 - 觅道文档MrDoc</title>
<link href="{% static 'layui/css/layui.css' %}" rel="stylesheet">
<link href="{% static 'mrdoc.css' %}" rel="stylesheet">
</head>

View File

@ -3,7 +3,7 @@
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %} - 后台管理 - MrDoc</title>
<title>{% block title %}{% endblock %} - 后台管理 - 觅道文档MrDoc</title>
<link rel="icon" href="{% static 'favicon_16.png' %}"/>
<link href="{% static 'layui/css/layui.css' %}?version={{mrdoc_version}}" rel="stylesheet">
<style>

View File

@ -38,11 +38,11 @@
</div>
<div class="layui-form-item">
<label class="layui-form-label">文集导出</label>
<label class="layui-form-label">文集下载</label>
<div class="layui-input-inline">
<input type="checkbox" name="enable_project_report" lay-skin="switch" lay-text="开启|关闭" {% if enable_project_report %}checked{% endif %}>
</div>
<div class="layui-form-mid layui-word-aux">开启此选项文集允许导出为EPUB和DOCX等格式文件文集拥有者可进行进一步控制文集是否开放导出</div>
<div class="layui-form-mid layui-word-aux">开启此选项文集允许导出为EPUB和DOCX等格式文件以供下载,文集拥有者可进行进一步控制特定文集是否开放导出</div>
</div>
<div class="layui-form-item">
@ -61,11 +61,17 @@
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">广告代码</label>
<label class="layui-form-label">广告位1</label>
<div class="layui-input-block">
<textarea name="ad_code" placeholder="如果需要在文档正文上方添加广告,请将广告相关代码输入至此,可以包含<script>标签" class="layui-textarea">{{ad_code}}</textarea>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">广告位2</label>
<div class="layui-input-block">
<textarea name="ad_code_2" placeholder="此广告位位于文档下方、分享按钮上方,可以包含<script>标签" class="layui-textarea">{{ad_code_2}}</textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">

View File

@ -7,6 +7,7 @@
<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<meta http-equiv="Cache-Control" content="max-age=7200" />
<link rel="icon" href="{% static 'favicon_16.png' %}"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>请输入访问码 - MrDoc</title>
<meta charset="utf-8" />

View File

@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="keywords" content="{% block keyword %}{% endblock %}mrdoc"/>
<meta name="description" content="{% block description %}{% endblock %}" />
<title>{% block title %}{% endblock %} - MrDoc</title>
<title>{% block title %}{% endblock %} - 觅道文档MrDoc</title>
<link href="{% static 'layui/css/layui.css' %}?version={{mrdoc_version}}" rel="stylesheet">
<link rel="stylesheet" href="{% static 'editor.md/css/editormd.css' %}?version={{mrdoc_version}}" />
<link rel="stylesheet" href="{% static 'katex/katex.min.css' %}?version={{mrdoc_version}}" />
@ -215,7 +215,7 @@
return [
"undo", "redo", "|",
"bold", "del", "italic", "quote","kaiSpan", "|",
"h1", "h2", "h3", "h4", "|",
"h1", "h2", "h3", "|",
"list-ul", "list-ol", "hr", "link", "reference-link", "|",
"mindmap","imgUpload", "attachment" ,"code", "code-block", "htmltable", "|","datetime", "emoji", "html-entities", "pagebreak", "|",
"watch", "preview", "|",
@ -383,6 +383,7 @@
flowChart : true, //开启流程图
sequenceDiagram : true, //开启序列图
imageUpload : true, //开启图片上传
codeFold :true, //代码折叠
htmlDecode : "link,style,script,iframe|on*", //解析部分HTML标签
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL : "{% url 'upload_doc_img' %}",

View File

@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="keywords" content="{% block keyword %}{% endblock %}mrdoc"/>
<meta name="description" content="{% block description %}{% endblock %}" />
<title>{% block title %}{% endblock %} - MrDoc</title>
<title>{% block title %}{% endblock %} - 觅道文档MrDoc</title>
<link href="{% static 'layui/css/layui.css' %}?version={{mrdoc_version}}" rel="stylesheet">
<link rel="stylesheet" href="{% static 'editor.md/css/editormd.css' %}?version={{mrdoc_version}}" />
<link rel="stylesheet" href="{% static 'katex/katex.min.css' %}?version={{mrdoc_version}}" />
@ -204,6 +204,17 @@
<!-- 正文结束 -->
</div>
<!-- 广告代码开始 -->
{% if debug %}
{% else %}
{% if ad_code %}
<div class="ad-code">
{{ ad_code_2 | safe }}
</div>
{% endif %}
{% endif %}
<!-- 广告代码结束 -->
{% block doc_previous_next %}{% endblock %}
<!-- 社交分享 -->
<div class="share-div" style="margin-top: 10px;padding:10px;text-align: center;background-color: #fafafa">

View File

@ -1,9 +1,11 @@
{% load staticfiles %}
<div class="layui-header layui-fluid">
<div class="" style="display:flex;flex-direction:row;justify-content:space-between;">
<!-- LOGO -->
<div class="">
<a class="logo" href="{% url 'pro_list' %}">
<h1><strong>MrDoc</strong></h1>
<img src="{% static 'logo.jpg' %}" style="height: 32px;width: auto;">
<!-- <h1><strong>MrDoc</strong></h1> -->
</a>
</div>
<!-- 搜索框 -->

View File

@ -4,7 +4,7 @@
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %} - 个人中心 - MrDoc</title>
<title>{% block title %}{% endblock %} - 个人中心 - 觅道文档MrDoc</title>
<link href="{% static 'layui/css/layui.css' %}?version={{mrdoc_version}}" rel="stylesheet">
<link href="{% static 'mrdoc-admin.css' %}?version={{mrdoc_version}}" rel="stylesheet">
<link href="{% static 'viewerjs/viewer.css' %}?version={{mrdoc_version}}" rel="stylesheet">
@ -36,10 +36,19 @@
.layui-input{
height: 30px !important;
}
/* layui引用文本样式 */
.layui-elem-quote{
border-left: 5px solid #1E9FFF !important;
}
/* layui单选框样式 */
.layui-form-radio>i:hover, .layui-form-radioed>i{
color: #1E9FFF;
}
/* 开关样式 */
.layui-form-onswitch{
border-color: #1E9FFF;
background-color: #1E9FFF;
}
</style>
</head>
<body class="layui-layout-body">

View File

@ -53,10 +53,11 @@
<td>{{ pro.id | get_doc_count }}</td>
<td>{{ pro.create_time }}</td>
<td>
{% if pro.id|get_report_status == 1 %}
<span>允许</span> <a href="{% url 'modify_pro_download' pro.id %}" title="修改前台下载"><i class="layui-icon layui-icon-edit"></i></a>
{% if pro.id|report_status_epub == 1 or pro.id|report_status_pdf == 1 %}
<span style="color: #01AAED;">允许</span> <a href="{% url 'modify_pro_download' pro.id %}" title="修改前台下载"><i class="layui-icon layui-icon-edit"></i></a>
{% else %}
<span>禁止</span> <a href="{% url 'modify_pro_download' pro.id %}" title="修改前台下载"><i class="layui-icon layui-icon-edit"></i></a>
<span style="color: hotpink;">禁止</span> <a href="{% url 'modify_pro_download' pro.id %}" title="修改前台下载"><i class="layui-icon layui-icon-edit"></i></a>
{% endif %}
</td>
<td>

View File

@ -1,10 +1,10 @@
{% extends 'app_doc/manage_base.html' %}
{% load staticfiles %}
{% block title %}文集下载状态管理{% endblock %}
{% block title %}文集下载状态管理 - {{pro.name}}{% endblock %}
{% block content %}
{% if enable_project_report %}
{% else %}
<blockquote class="layui-elem-quote">当前站点未启用前台文集下载功能,修改操作将在功能启用后生效!</blockquote>
<blockquote class="layui-elem-quote">站点管理员未启用文集导出下载文件功能,相关操作将在此功能启用后生效!</blockquote>
{% endif %}
<div class="layui-row" style="margin-bottom: 10px;padding-left:15px;">
<span class="layui-breadcrumb" lay-separator=">">
@ -33,22 +33,76 @@
<label class="layui-form-label">EPUB下载</label>
<div class="layui-input-block">
<input type="checkbox" name="download_epub"
{% if pro.id|get_report_status == 1 %} checked {%endif%}
{% if pro.id|report_status_epub == 1 %} checked {%endif%}
lay-skin="switch" lay-text="允许|禁止">
<!-- 判断后台是否开启导出,如果开启,则显示 -->
{% if enable_project_report %}
<a href="javascript:void(0);" onclick="reportFile('{{pro.id}}','epub')" style=""><i class="layui-icon layui-icon-refresh"></i><u>生成或更新EPUB文件</u></a>
{% if project_files %}
{% for file in project_files %}
{% if file.file_type == 'epub' %}
| <a href="{{file.file_path}}" target="_blank"><i class="layui-icon layui-icon-download-circle"></i><u>下载文集EPUB文件</u></a>
{% else %}
{% endif %}
{% endfor %}
{% else %}
| <span style="color: hotpink;">未生成文集导出文件</span>
{% endif %}
{% endif %}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">PDF下载</label>
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="formDemo">立即修改</button>
<input type="checkbox" name="download_pdf"
{% if pro.id|report_status_pdf == 1 %} checked {%endif%}
lay-skin="switch" lay-text="允许|禁止">
<!-- 判断后台是否开启导出,如果开启,则显示 -->
{% if enable_project_report %}
<a href="javascript:void(0);" onclick="reportFile('{{pro.id}}','pdf')" style=""><i class="layui-icon layui-icon-refresh"></i><u>生成或更新PDF文件</u></a>
{% if project_files %}
{% for file in project_files %}
{% if file.file_type == 'pdf' %}
| <a href="{{file.file_path}}" target="_blank"><i class="layui-icon layui-icon-download-circle"></i><u>下载文集PDF文件</u></a>
{% else %}
{% endif %}
{% endfor %}
{% else %}
| <span style="color: hotpink;">未生成文集导出文件</span>
{% endif %}
{% endif %}
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal layui-btn-sm" lay-submit lay-filter="formDemo">立即修改</button>
</div>
</div>
<div class="layui-form-item">
<blockquote class="layui-elem-quote">注意开启某类型文件下载后请先点击“生成或更新XXX文件”文集文档中如果包含公式、流程图、时序图、脑图等内容将会延长生成时间请耐心等待</blockquote>
</div>
</form>
</div>
{% endblock %}
{% block custom_script %}
<script>
var form = layui.form;
reportFile = function(pro_id,types){
layer.load(1)
var data = {'pro_id':pro_id,'types':types};
$.post("{% url 'genera_project_file' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
layer.msg("生成完成")
window.location.reload();
}else{
layer.msg("生成出错,请稍后重试")
}
})
}
</script>
{% endblock %}

View File

@ -57,7 +57,7 @@
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="formDemo">立即修改</button>
<button class="layui-btn layui-btn-normal layui-btn-sm" lay-submit lay-filter="formDemo">立即修改</button>
</div>
</div>
</form>

View File

@ -7,10 +7,10 @@
<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<meta http-equiv="Cache-Control" content="max-age=7200" />
<meta name="keywords" content="mrdoc,markdown,文档写作,在线教程,Python文档系统,django应用"/>
<meta name="description" content="MrDoc是由州的先生zmister.com开发基于Python的Django框架的在线文档写作系统,适合作为个人和小型团队的文档、笔记和知识管理工具。" />
<meta name="keywords" content="觅道文档,mrdoc,markdown,文档写作,在线教程,Python文档系统,django应用"/>
<meta name="description" content="觅道文档(MrDoc)是由州的先生zmister.com开发基于Python的Django框架的在线文档系统适合作为个人和小型团队的文档、笔记和知识管理工具。" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>MrDoc - 一个简单的文档写作系统</title>
<title>觅道文档(MrDoc) - 一个简单的在线文档系统</title>
<link href="{% static 'layui/css/layui.css' %}" rel="stylesheet">
<link href="{% static 'mrdoc.css' %}?version={{mrdoc_version}}" rel="stylesheet">
<link rel="icon" href="{% static 'favicon_16.png' %}"/>

View File

@ -74,10 +74,15 @@
<h3>文集下载:</h3>
<p>
<span class="layui-breadcrumb" lay-separator="|">
{% if allow_epub_download == 1 %}
<a href="javascript:void(0);" onclick="reportFile('{{project.id}}','epub')"><i class="fa fa-download"></i> <u>EPUB电子书</u></a>
{% if allow_download %}
{% if allow_download.allow_epub == 1 %}
<a href="javascript:void(0);" onclick="reportFile('{{project.id}}','epub')"><i class="fa fa-download"></i> <u>EPUB电子书</u></a>
{% endif %}
{% if allow_download.allow_pdf == 1 %}
<a href="javascript:void(0);" onclick="reportFile('{{project.id}}','pdf')"><i class="fa fa-download"></i> <u>PDF电子书</u></a>
{% endif %}
{% else %}
<a>文集作者未开放此文集的任何格式下载!</a>
<a>文集作者未开放此文集的任何格式下载!</a>
{% endif %}
<!--<a href=""><i class="fa fa-download"></i> <u>DOCX文档</u></a>-->
</span>
@ -85,13 +90,15 @@
</div>
<script>
reportFile = function(pro_id,types){
layer.load(1)
var data = {'pro_id':pro_id,'types':types};
$.post("{% url 'report_file' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
//文件下载提示
downloadFile(r.data)
}else{
layer.msg("导出文件出错")
layer.msg(r.data)
}
})
}
@ -102,7 +109,7 @@
title:"下载导出文档",
area:"300px",
id:"downloadMd",
content:'<p style="text-align:center;color:red;">请尽快下载,避免失效!</p><br><a class="layui-btn layui-btn-fluid download-md-link" href="'+ download_link + '" download="" >点击下载文件</a>',
content:'<p style="text-align:center;color:red;">请尽快下载,避免失效!</p><br><a class="layui-btn layui-btn-normal layui-btn-fluid download-md-link" href="'+ download_link + '" download="" >点击下载文件</a>',
//btn:['确定','取消'], //添加按钮
//btnAlign:'c', //按钮居中
success: function (layero, index) {