新增脑图功能、首页筛选、优化表格输入、交互细节

This commit is contained in:
yangjian 2020-04-20 07:51:25 +08:00
parent c789e1008a
commit c64e62749b
37 changed files with 9109 additions and 496 deletions

View File

@ -1,5 +1,12 @@
## 版本更新记录
### v0.4.2 2020-04-20
- 添加思维导图功能的支持,可以在文档编辑器通过图标和`mindmap`标识代码块来创建脑图;
- 首页添加文集筛选、文集排序和文集网格/列表视图切换;
- 文档编辑器优化表格插入按钮新增粘贴Excel内容转Markdown的功能
- 优化交互体验;
### v0.4.1
- 添加文档历史版本功能,可在修改文档时对比查看和选择恢复文档的历史版本;

View File

@ -25,7 +25,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.1'
VERSIONS = '0.4.2'
ALLOWED_HOSTS = ['*']

View File

@ -14,22 +14,27 @@
## 特性:
- 简洁的站点与用户系统
- 支持用户注册、用户登录、用户管理、管理员等控制等功能;
- 支持注册邀请码配置,支持全站关闭注册;
- 支持广告代码、统计代码自定义配置;
- **站点与用户系统**
- 支持用户注册、用户登录、用户管理、管理员等基础用户功能;
- 支持注册邀请码配置、广告代码配置、统计代码配置、邮箱找回密码、全站关闭注册等管理功能;
- 结构清晰地文档系统
- 基于文集进行文档撰写和阅读拥有文集、文档、文档模板、图片和附件5大模块
- 使用基于`Editormd``Markdown`编辑器并扩展,以`Markdown`语法进行文档写作;
- 两栏式文档阅读页面、三级目录层级显示,文档阅读字体缩放,字体类型切换,页面社交分享,移动端阅读优化;
- 支持文集后台导出打包`markdown`文本格式`.md`文件、前台导出为`EPUB`等格式文件;
- 基于文集进行文档权限控制提供公开、私密、指定用户可见、访问码可见4种权限模式
- 支持基于账户的`API`接口,可以借助账户`token`通过`API`获取文集、上传图片和创建文档;
- 支持文集协作功能,一个文集可以拥有一个创建者和多个协作者,可灵活选择协作权限;
- 支持文档历史版本功能;
- **文档系统**
- 基于文集进行文档撰写和阅读,拥有**文集**、**文档**、**文档模板**、**图片**和**附件**5大模块
- 使用基于`Editormd``Markdown`编辑器并扩展,以`Markdown`语法进行文档写作:
- 支持**基础Markdown语法**
- 支持**GFM语法**
- 优化**图片上传**,支持粘贴上传、本地文件上传和图片管理插入
- 优化**表格写入**支持编辑HTML表格转为Markdown、Excel内容粘贴为Markdown
- 支持**思维导图**以Markdown的语法创建思维导图
- 支持流程图、时序图的绘制;
- 两栏式**文档阅读**页面、三级目录层级显示,文档阅读字体缩放,字体类型切换,页面社交分享,移动端阅读优化;
- 支持文集后台**导出打包**`markdown`文本格式`.md`文件、前台导出为`EPUB`等格式文件;
- 基于文集进行**文档权限**控制提供公开、私密、指定用户可见、访问码可见4种权限模式
- 支持基于账户的**`API`接口**,可以借助账户`token`通过`API`获取文集、上传图片和创建文档;
- 支持**文集协作**功能,一个文集可以拥有一个创建者和多个协作者,可灵活选择协作权限;
- 支持**文档历史版本**功能,可以查看和对比历史版本与现有版本的差异,恢复某个历史版本为当前版本;
当前版本为:**v0.4.1**,版本发布时间为**2020-04-11**
当前版本为:**v0.4.2**,版本发布时间为**2020-04-20**
完整更新记录详见:[CHANGES.md](./CHANGES.md)
@ -37,9 +42,9 @@
## 应用核心依赖环境
MrDoc基于Python语言的Django Web框架配合前端的LayUI、JQuery等库进行开发。
`MrDoc`基于`Python`语言的`Django Web`框架配合前端的`LayUI``JQuery`等库进行开发。
在Django2.1、2.2和Python3.5、3.6、3.7上测试运行良好。
`MrDoc``Python3.6` + `Django 2.2`上进行开发并且在Django 2.1、2.2和Python3.5、3.6、3.7上测试运行良好在其他环境下运行MrDoc不排除有未知的异常。
## 简明安装教程
@ -53,7 +58,7 @@ pip install -r requirements.txt
默认情况下MrDoc使用Django的SQLite数据库如果你使用Sqlite数据库则无需另外配置数据库。
如果有配置其他数据库的需求,请在/MrDoc/MrDoc目录下打开settings.py文件在约80行的位置将如下代码
```
```python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
@ -62,7 +67,7 @@ DATABASES = {
}
```
按照自己数据库的信息将其修改如下格式下面以MySQL为例
```
```python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 使用的数据库后端

View File

@ -23,43 +23,141 @@ def validateTitle(title):
return new_title
# 文集列表
def project_list(request):
kw = request.GET.get('kw','')
if kw == '':
# 登录用户
if request.user.is_authenticated:
# 用户的协作文集
colla_list = [i.project.id for i in ProjectCollaborator.objects.filter(user=request.user)]
# 查询所有可显示的文集
project_list = Project.objects.filter(
Q(role__in=[0,3]) | \
Q(role=2,role_value__contains=str(request.user.username)) | \
Q(create_user=request.user) | \
Q(id__in=colla_list)
)
else:
# 非登录用户只显示公开文集和需要访问码的文集
project_list = Project.objects.filter(role__in=[0,3])
kw = request.GET.get('kw','') # 搜索词
sort = request.GET.get('sort',0) # 排序,0表示按时间升序排序1表示按时间降序排序默认为0
role = request.GET.get('role',-1) # 筛选文集权限,默认为显示所有可显示的文集
# 是否排序
if sort in ['',0,'0']:
sort_str = ''
else:
# 登录用户
if request.user.is_authenticated:
# 用户的协作文集
colla_list = [i.project.id for i in ProjectCollaborator.objects.filter(user=request.user)]
# 查询所有可显示的文集
project_list = Project.objects.filter(
Q(role__in=[0, 3]) | \
Q(role=2, role_value__contains=str(request.user.username)) | \
Q(create_user=request.user) | \
Q(id__in=colla_list),
Q(name__icontains=kw) | Q(intro__icontains=kw)
)
sort_str = '-'
# 是否搜索
if kw == '':
is_kw = False
else:
is_kw = True
# 是否认证
if request.user.is_authenticated:
is_auth = True
else:
is_auth = False
# 是否筛选
if role in ['',-1,'-1']:
is_role = False
role_list = [0,3]
else:
is_role = True
# 没有搜索 and 认证用户 and 没有筛选
if (is_kw is False) and (is_auth) and (is_role is False):
colla_list = [i.project.id for i in ProjectCollaborator.objects.filter(user=request.user)] # 用户的协作文集列表
project_list = Project.objects.filter(
Q(role__in=role_list) | \
Q(role=2,role_value__contains=str(request.user.username)) | \
Q(create_user=request.user) | \
Q(id__in=colla_list)
).order_by("{}create_time".format(sort_str))
# 没有搜索 and 认证用户 and 有筛选
elif (is_kw is False ) and (is_auth) and (is_role):
if role in ['0',0]:
project_list = Project.objects.filter(role=0).order_by("{}create_time".format(sort_str))
elif role in ['1',1]:
project_list = Project.objects.filter(create_user=request.user,role=1).order_by("{}create_time".format(sort_str))
elif role in ['2',2]:
project_list = Project.objects.filter(role=2,role_value__contains=str(request.user.username)).order_by("{}create_time".format(sort_str))
elif role in ['3',3]:
project_list = Project.objects.filter(role=3).order_by("{}create_time".format(sort_str))
elif role in ['99',99]:
colla_list = [i.project.id for i in ProjectCollaborator.objects.filter(user=request.user)] # 用户的协作文集列表
project_list = Project.objects.filter(id__in=colla_list).order_by("{}create_time".format(sort_str))
else:
# 非登录用户只显示公开文集和需要访问码的文集
return render(request,'404.html')
# 没有搜索 and 游客 and 没有筛选
elif (is_kw is False) and (is_auth is False) and (is_role is False):
project_list = Project.objects.filter(role__in=[0,3]).order_by("{}create_time".format(sort_str))
# 没有搜索 and 游客 and 有筛选
elif (is_kw is False) and (is_auth is False) and (is_role):
if role in ['0',0]:
project_list = Project.objects.filter(role=0).order_by("{}create_time".format(sort_str))
elif role in ['3',3]:
project_list = Project.objects.filter(role=3).order_by("{}create_time".format(sort_str))
else:
return render(request,'404.html')
# 有搜索 and 认证用户 and 没有筛选
elif (is_kw) and (is_auth) and (is_role is False):
colla_list = [i.project.id for i in ProjectCollaborator.objects.filter(user=request.user)] # 用户的协作文集
# 查询所有可显示的文集
project_list = Project.objects.filter(
Q(role__in=[0, 3]) | \
Q(role=2, role_value__contains=str(request.user.username)) | \
Q(create_user=request.user) | \
Q(id__in=colla_list),
Q(name__icontains=kw) | Q(intro__icontains=kw)
).order_by('{}create_time'.format(sort_str))
# 有搜索 and 认证用户 and 有筛选
elif (is_kw) and (is_auth) and (is_role):
if role in ['0',0]:
project_list = Project.objects.filter(
Q(name__icontains=kw)|Q(intro__icontains=kw),
role=0
).order_by("{}create_time".format(sort_str))
elif role in ['1',1]:
project_list = Project.objects.filter(
Q(name__icontains=kw) | Q(intro__icontains=kw),
role__in=[0, 3]
)
create_user=request.user
).order_by("{}create_time".format(sort_str))
elif role in ['2',2]:
project_list = Project.objects.filter(
Q(name__icontains=kw) | Q(intro__icontains=kw),
role=2,
role_value__contains=str(request.user.username)
).order_by("{}create_time".format(sort_str))
elif role in ['3',3]:
project_list = Project.objects.filter(
Q(name__icontains=kw) | Q(intro__icontains=kw),
role=3
).order_by("{}create_time".format(sort_str))
elif role in ['99',99]:
colla_list = [i.project.id for i in ProjectCollaborator.objects.filter(user=request.user)] # 用户的协作文集列表
project_list = Project.objects.filter(
Q(name__icontains=kw) | Q(intro__icontains=kw),
id__in=colla_list
).order_by("{}create_time".format(sort_str))
else:
return render(request,'404.html')
# 有搜索 and 游客 and 没有筛选
elif (is_kw) and (is_auth is False) and (is_role is False):
project_list = Project.objects.filter(
Q(name__icontains=kw) | Q(intro__icontains=kw),
role__in=[0, 3]
).order_by("{}create_time".format(sort_str))
# 有搜索 and 游客 and 有筛选
elif (is_kw) and (is_auth is False) and (is_role):
if role in ['0',0]:
project_list = Project.objects.filter(
Q(name__icontains=kw) | Q(intro__icontains=kw),
role=0
).order_by("{}create_time".format(sort_str))
elif role in ['3',3]:
project_list = Project.objects.filter(
Q(name__icontains=kw) | Q(intro__icontains=kw),
role=3
).order_by("{}create_time".format(sort_str))
else:
return render(request,'404.html')
# 分页处理
paginator = Paginator(project_list, 12)
page = request.GET.get('page', 1)
@ -71,7 +169,6 @@ def project_list(request):
projects = paginator.page(paginator.num_pages)
return render(request, 'app_doc/pro_list.html', locals())
# 创建文集
@login_required()
def create_project(request):
@ -1291,6 +1388,10 @@ def manage_attachment(request):
if attachment:
attachment_name = attachment.name
attachment_size = sizeFormat(attachment.size)
# 限制附件大小在50mb以内
if attachment.size > 52428800:
return JsonResponse({'status':False,'data':'文件大小超出限制'})
# 限制附件为ZIP格式文件
if attachment_name.endswith('.zip'):
a = Attachment.objects.create(
file_name = attachment_name,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 170 KiB

View File

@ -168,6 +168,7 @@
tex : false, // TeX(LaTeX), based on KaTeX
flowChart : false, // flowChart.js only support IE9+
sequenceDiagram : false, // sequenceDiagram.js only support IE9+
mindMap : true, // 脑图
previewCodeHighlight : true,
toolbar : true, // show/hide toolbar
@ -1492,7 +1493,6 @@
*/
katexRender : function() {
if (timer === null)
{
return this;
@ -1507,6 +1507,24 @@
return this;
},
/**
* 解析思维导图 - 2020-04-12
*
* @returns {editormd} 返回editormd的实例对象
*/
mindmapRender:function(){
// console.log("开始解析脑图")
this.previewContainer.find(".mindmap").each(function(){
var mmap = $(this);
var md_data = window.markmap.transform(mmap.text().trim());
window.markmap.markmap("svg#"+this.id,md_data)
//drawMindMap(mmap[0]) // kityminder的实现
});
return this;
},
/**
* 解析和渲染流程图及时序图
@ -1529,7 +1547,7 @@
return this;
}
previewContainer.find(".flowchart").flowChart();
previewContainer.find(".flowchart").flowChart();
}
if (settings.sequenceDiagram) {
@ -1999,6 +2017,7 @@
emailLink : settings.emailLink, // for mail address auto link
flowChart : settings.flowChart,
sequenceDiagram : settings.sequenceDiagram,
mindMap : settings.mindMap,
previewCodeHighlight : settings.previewCodeHighlight,
};
@ -2032,13 +2051,13 @@
this.htmlTextarea.text(newMarkdownDoc);
}
if(settings.watch || (!settings.watch && state.preview))
if(settings.watch || (!settings.watch && state.preview))//如果开启了预览
{
previewContainer.html(newMarkdownDoc);
this.previewCodeHighlight();
if (settings.toc)
if (settings.toc) // 渲染目录
{
var tocContainer = (settings.tocContainer === "") ? previewContainer : $(settings.tocContainer);
var tocMenu = tocContainer.find("." + this.classPrefix + "toc-menu");
@ -2063,7 +2082,7 @@
}
}
if (settings.tex)
if (settings.tex) // 渲染公式
{
if (!editormd.kaTeXLoaded && settings.autoLoadModules)
{
@ -2078,8 +2097,17 @@
editormd.$katex = katex;
this.katexRender();
}
}
}
// 渲染脑图
if(settings.mindMap){
setTimeout(function(){
_this.mindmapRender();
},10)
}
// 渲染流程图和时序图
if (settings.flowChart || settings.sequenceDiagram)
{
flowchartTimer = setTimeout(function(){
@ -3633,10 +3661,21 @@
else if ( lang === "math" || lang === "latex" || lang === "katex")
{
return "<p class=\"" + editormd.classNames.tex + "\">" + code + "</p>";
}
else if (/^mindmap/i.test(lang)){
  var len = 9 || 32;
  var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
  var maxPos = $chars.length;
  var map_id = '';
  for (var i = 0; i < len; i++) {
    map_id += $chars.charAt(Math.floor(Math.random() * maxPos));
}
// 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>";
}
else
{
return marked.Renderer.prototype.code.apply(this, arguments);
}
};
@ -3913,6 +3952,7 @@
taskList : false, // Github Flavored Markdown task lists
emoji : false,
flowChart : false,
mindMap : true, //百度脑图
sequenceDiagram : false,
previewCodeHighlight : true
};
@ -3944,6 +3984,7 @@
emailLink : settings.emailLink, // for mail address auto link
flowChart : settings.flowChart,
sequenceDiagram : settings.sequenceDiagram,
mindMap : settings.mindMap, // 思维导图
previewCodeHighlight : settings.previewCodeHighlight,
};
@ -4020,7 +4061,6 @@
tex.find(".katex").css("font-size", "1.6em");
});
};
if (settings.autoLoadKaTeX && !editormd.$katex && !editormd.kaTeXLoaded)
{
this.loadKaTeX(function() {
@ -4034,6 +4074,20 @@
katexHandle();
}
}
// 前台渲染脑图
if(settings.mindMap){
// console.log("前台渲染脑图")
var mindmapHandle = function(){
div.find(".mindmap").each(function(){
var mmap = $(this);
var md_data = window.markmap.transform(mmap.text().trim());
window.markmap.markmap("svg#"+this.id,md_data)
//drawMindMap(mmap[0]) // kityminder的实现
});
}
mindmapHandle();
}
div.getMarkdown = function() {
return saveTo.val();
@ -4181,9 +4235,9 @@
// You can custom KaTeX load url.
editormd.katexURL = {
//css : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min",
css : "/static/katex/katex.min.css",
css : "/static/katex/katex.min",
//js : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min"
js : "/static/katex/katex.min.js",
js : "/static/katex/katex.min",
};
editormd.kaTeXLoaded = false;

View File

@ -3622,7 +3622,45 @@
else if ( lang === "math" || lang === "latex" || lang === "katex")
{
return "<p class=\"" + editormd.classNames.tex + "\">" + code + "</p>";
}
}
else if (/^mindmap/i.test(lang)){
lang=lang+" ";//加一个空格,便于解析各参数使用
//各参数解析开始
var sizeps = lang.match(/(?<=size:)(mindmap-sm|mindmap-md|mindmap-lg)(?= )/i);
var Templateps = lang.match(/(?<=Template:)(fresh-blue|filetree|fish-bone|right|structure|tianpan)(?= )/i);
var Themeps = lang.match(/(?<=Theme:)(classic|classic-compact|fish|fresh-blue|fresh-blue-compat|fresh-green|fresh-green-compat|fresh-pink|fresh-pink-compat|fresh-purple|fresh-purple-compat|fresh-red|fresh-red-compat|fresh-soil|fresh-soil-compat|snow|snow-compact|tianpan|tianpan-compact|wire)(?= )/i);
var protocolps=lang.match(/(?<=protocol:)(json|text|markdown|list)(?= )/i);
var tmpshowps=lang.match(/(?<=tmpshow:)(true)(?= )/i);
var size=(sizeps!== null)?sizeps[0]:"mindmap-md";
var Theme=(Themeps!== null)?Themeps[0]:"fresh-blue";
var protocol=(protocolps!== null)?protocolps[0]:"markdown";
var Template=(Templateps!== null)?Templateps[0]:"default";
var tmpshow=(tmpshowps!== null)?"":"style=\"display:none;\"";
//参数解析结束
//生成两个div其中一个存放参数一个存放待生成的数据。
if(protocol=="list"){
code=marked(code);
}
else {
//先将code解析为json数据并添加主题和模板
//如果不先解析按照官方文档使用minder.execCommand('Template', "right");
//或minder.useTemplate;minder.setTemplate;
//等均没有效果,需要单独添加一个按钮或标签,等加载完才可以改变,有点无语。
var minder=new kityminder.Minder();
try {
var tmpcode = minder.decodeData(protocol,code);
tmpcode=tmpcode.fulfillValue;
tmpcode.template=Template;
tmpcode.theme=Theme;
code=JSON.stringify(tmpcode);
}catch(e){}
}
var mindmapoption="<div class=\"mindmapoption\" style=\"display:none;\" >" + lang + "</div>";
var midmaptmpdiv="<div class=\"mindmaptmp\"" +tmpshow+" >" + code + "</div>";
return "<div class=\"mindmap "+size+"\">"+mindmapoption+midmaptmpdiv+"</div>";
}
else
{
@ -4183,6 +4221,9 @@
*/
editormd.loadKaTeX = function (callback) {
// editormd.loadCSS(editormd.katexURL.css, function(){
// editormd.loadScript(editormd.katexURL.js, callback || function(){});
// });
editormd.loadCSS(editormd.katexURL.css, function(){
editormd.loadScript(editormd.katexURL.js, callback || function(){});
});

2
static/mindmap/d3@5.js vendored Normal file

File diff suppressed because one or more lines are too long

7496
static/mindmap/transform.js Normal file

File diff suppressed because it is too large Load Diff

883
static/mindmap/view.js Normal file
View File

@ -0,0 +1,883 @@
/*! markmap-lib v0.4.2 | MIT License */
(function (exports, d3) {
'use strict';
function count(node) {
var sum = 0,
children = node.children,
i = children && children.length;
if (!i) sum = 1;
else while (--i >= 0) sum += children[i].value;
node.value = sum;
}
function node_count() {
return this.eachAfter(count);
}
function node_each(callback) {
var node = this, current, next = [node], children, i, n;
do {
current = next.reverse(), next = [];
while (node = current.pop()) {
callback(node), children = node.children;
if (children) for (i = 0, n = children.length; i < n; ++i) {
next.push(children[i]);
}
}
} while (next.length);
return this;
}
function node_eachBefore(callback) {
var node = this, nodes = [node], children, i;
while (node = nodes.pop()) {
callback(node), children = node.children;
if (children) for (i = children.length - 1; i >= 0; --i) {
nodes.push(children[i]);
}
}
return this;
}
function node_eachAfter(callback) {
var node = this, nodes = [node], next = [], children, i, n;
while (node = nodes.pop()) {
next.push(node), children = node.children;
if (children) for (i = 0, n = children.length; i < n; ++i) {
nodes.push(children[i]);
}
}
while (node = next.pop()) {
callback(node);
}
return this;
}
function node_sum(value) {
return this.eachAfter(function(node) {
var sum = +value(node.data) || 0,
children = node.children,
i = children && children.length;
while (--i >= 0) sum += children[i].value;
node.value = sum;
});
}
function node_sort(compare) {
return this.eachBefore(function(node) {
if (node.children) {
node.children.sort(compare);
}
});
}
function node_path(end) {
var start = this,
ancestor = leastCommonAncestor(start, end),
nodes = [start];
while (start !== ancestor) {
start = start.parent;
nodes.push(start);
}
var k = nodes.length;
while (end !== ancestor) {
nodes.splice(k, 0, end);
end = end.parent;
}
return nodes;
}
function leastCommonAncestor(a, b) {
if (a === b) return a;
var aNodes = a.ancestors(),
bNodes = b.ancestors(),
c = null;
a = aNodes.pop();
b = bNodes.pop();
while (a === b) {
c = a;
a = aNodes.pop();
b = bNodes.pop();
}
return c;
}
function node_ancestors() {
var node = this, nodes = [node];
while (node = node.parent) {
nodes.push(node);
}
return nodes;
}
function node_descendants() {
var nodes = [];
this.each(function(node) {
nodes.push(node);
});
return nodes;
}
function node_leaves() {
var leaves = [];
this.eachBefore(function(node) {
if (!node.children) {
leaves.push(node);
}
});
return leaves;
}
function node_links() {
var root = this, links = [];
root.each(function(node) {
if (node !== root) { // Dont include the roots parent, if any.
links.push({source: node.parent, target: node});
}
});
return links;
}
function hierarchy(data, children) {
var root = new Node(data),
valued = +data.value && (root.value = data.value),
node,
nodes = [root],
child,
childs,
i,
n;
if (children == null) children = defaultChildren;
while (node = nodes.pop()) {
if (valued) node.value = +node.data.value;
if ((childs = children(node.data)) && (n = childs.length)) {
node.children = new Array(n);
for (i = n - 1; i >= 0; --i) {
nodes.push(child = node.children[i] = new Node(childs[i]));
child.parent = node;
child.depth = node.depth + 1;
}
}
}
return root.eachBefore(computeHeight);
}
function node_copy() {
return hierarchy(this).eachBefore(copyData);
}
function defaultChildren(d) {
return d.children;
}
function copyData(node) {
node.data = node.data.data;
}
function computeHeight(node) {
var height = 0;
do node.height = height;
while ((node = node.parent) && (node.height < ++height));
}
function Node(data) {
this.data = data;
this.depth =
this.height = 0;
this.parent = null;
}
Node.prototype = hierarchy.prototype = {
constructor: Node,
count: node_count,
each: node_each,
eachAfter: node_eachAfter,
eachBefore: node_eachBefore,
sum: node_sum,
sort: node_sort,
path: node_path,
ancestors: node_ancestors,
descendants: node_descendants,
leaves: node_leaves,
links: node_links,
copy: node_copy
};
var version = "2.1.1";
const defaults = Object.freeze({
children: data => data.children,
nodeSize: node => node.data.size,
spacing: 0,
});
// Create a layout function with customizable options. Per D3-style, the
// options can be set at any time using setter methods. The layout function
// will compute the tree node positions based on the options in effect at the
// time it is called.
function flextree(options) {
const opts = Object.assign({}, defaults, options);
function accessor(name) {
const opt = opts[name];
return typeof opt === 'function' ? opt : () => opt;
}
function layout(tree) {
const wtree = wrap(getWrapper(), tree, node=>node.children);
wtree.update();
return wtree.data;
}
function getFlexNode() {
const nodeSize = accessor('nodeSize');
const spacing = accessor('spacing');
return class FlexNode extends hierarchy.prototype.constructor {
constructor(data) {
super(data);
}
copy() {
const c = wrap(this.constructor, this, node=>node.children);
c.each(node => node.data = node.data.data);
return c;
}
get size() { return nodeSize(this); }
spacing(oNode) { return spacing(this, oNode); }
get nodes() { return this.descendants(); }
get xSize() { return this.size[0]; }
get ySize() { return this.size[1]; }
get top() { return this.y; }
get bottom() { return this.y + this.ySize; }
get left() { return this.x - this.xSize / 2; }
get right() { return this.x + this.xSize / 2; }
get root() {
const ancs = this.ancestors();
return ancs[ancs.length - 1];
}
get numChildren() {
return this.hasChildren ? this.children.length : 0;
}
get hasChildren() { return !this.noChildren; }
get noChildren() { return this.children === null; }
get firstChild() {
return this.hasChildren ? this.children[0] : null;
}
get lastChild() {
return this.hasChildren ? this.children[this.numChildren - 1] : null;
}
get extents() {
return (this.children || []).reduce(
(acc, kid) => FlexNode.maxExtents(acc, kid.extents),
this.nodeExtents);
}
get nodeExtents() {
return {
top: this.top,
bottom: this.bottom,
left: this.left,
right: this.right,
};
}
static maxExtents(e0, e1) {
return {
top: Math.min(e0.top, e1.top),
bottom: Math.max(e0.bottom, e1.bottom),
left: Math.min(e0.left, e1.left),
right: Math.max(e0.right, e1.right),
};
}
};
}
function getWrapper() {
const FlexNode = getFlexNode();
const nodeSize = accessor('nodeSize');
const spacing = accessor('spacing');
return class extends FlexNode {
constructor(data) {
super(data);
Object.assign(this, {
x: 0, y: 0,
relX: 0, prelim: 0, shift: 0, change: 0,
lExt: this, lExtRelX: 0, lThr: null,
rExt: this, rExtRelX: 0, rThr: null,
});
}
get size() { return nodeSize(this.data); }
spacing(oNode) { return spacing(this.data, oNode.data); }
get x() { return this.data.x; }
set x(v) { this.data.x = v; }
get y() { return this.data.y; }
set y(v) { this.data.y = v; }
update() {
layoutChildren(this);
resolveX(this);
return this;
}
};
}
function wrap(FlexClass, treeData, children) {
const _wrap = (data, parent) => {
const node = new FlexClass(data);
Object.assign(node, {
parent,
depth: parent === null ? 0 : parent.depth + 1,
height: 0,
length: 1,
});
const kidsData = children(data) || [];
node.children = kidsData.length === 0 ? null
: kidsData.map(kd => _wrap(kd, node));
if (node.children) {
Object.assign(node, node.children.reduce(
(hl, kid) => ({
height: Math.max(hl.height, kid.height + 1),
length: hl.length + kid.length,
}), node
));
}
return node;
};
return _wrap(treeData, null);
}
Object.assign(layout, {
nodeSize(arg) {
return arguments.length ? (opts.nodeSize = arg, layout) : opts.nodeSize;
},
spacing(arg) {
return arguments.length ? (opts.spacing = arg, layout) : opts.spacing;
},
children(arg) {
return arguments.length ? (opts.children = arg, layout) : opts.children;
},
hierarchy(treeData, children) {
const kids = typeof children === 'undefined' ? opts.children : children;
return wrap(getFlexNode(), treeData, kids);
},
dump(tree) {
const nodeSize = accessor('nodeSize');
const _dump = i0 => node => {
const i1 = i0 + ' ';
const i2 = i0 + ' ';
const {x, y} = node;
const size = nodeSize(node);
const kids = (node.children || []);
const kdumps = (kids.length === 0) ? ' ' :
`,${i1}children: [${i2}${kids.map(_dump(i2)).join(i2)}${i1}],${i0}`;
return `{ size: [${size.join(', ')}],${i1}x: ${x}, y: ${y}${kdumps}},`;
};
return _dump('\n')(tree);
},
});
return layout;
}
flextree.version = version;
const layoutChildren = (w, y = 0) => {
w.y = y;
(w.children || []).reduce((acc, kid) => {
const [i, lastLows] = acc;
layoutChildren(kid, w.y + w.ySize);
// The lowest vertical coordinate while extreme nodes still point
// in current subtree.
const lowY = (i === 0 ? kid.lExt : kid.rExt).bottom;
if (i !== 0) separate(w, i, lastLows);
const lows = updateLows(lowY, i, lastLows);
return [i + 1, lows];
}, [0, null]);
shiftChange(w);
positionRoot(w);
return w;
};
// Resolves the relative coordinate properties - relX and prelim --
// to set the final, absolute x coordinate for each node. This also sets
// `prelim` to 0, so that `relX` for each node is its x-coordinate relative
// to its parent.
const resolveX = (w, prevSum, parentX) => {
// A call to resolveX without arguments is assumed to be for the root of
// the tree. This will set the root's x-coord to zero.
if (typeof prevSum === 'undefined') {
prevSum = -w.relX - w.prelim;
parentX = 0;
}
const sum = prevSum + w.relX;
w.relX = sum + w.prelim - parentX;
w.prelim = 0;
w.x = parentX + w.relX;
(w.children || []).forEach(k => resolveX(k, sum, w.x));
return w;
};
// Process shift and change for all children, to add intermediate spacing to
// each child's modifier.
const shiftChange = w => {
(w.children || []).reduce((acc, child) => {
const [lastShiftSum, lastChangeSum] = acc;
const shiftSum = lastShiftSum + child.shift;
const changeSum = lastChangeSum + shiftSum + child.change;
child.relX += changeSum;
return [shiftSum, changeSum];
}, [0, 0]);
};
// Separates the latest child from its previous sibling
/* eslint-disable complexity */
const separate = (w, i, lows) => {
const lSib = w.children[i - 1];
const curSubtree = w.children[i];
let rContour = lSib;
let rSumMods = lSib.relX;
let lContour = curSubtree;
let lSumMods = curSubtree.relX;
let isFirst = true;
while (rContour && lContour) {
if (rContour.bottom > lows.lowY) lows = lows.next;
// How far to the left of the right side of rContour is the left side
// of lContour? First compute the center-to-center distance, then add
// the "spacing"
const dist =
(rSumMods + rContour.prelim) - (lSumMods + lContour.prelim) +
rContour.xSize / 2 + lContour.xSize / 2 +
rContour.spacing(lContour);
if (dist > 0 || (dist < 0 && isFirst)) {
lSumMods += dist;
// Move subtree by changing relX.
moveSubtree(curSubtree, dist);
distributeExtra(w, i, lows.index, dist);
}
isFirst = false;
// Advance highest node(s) and sum(s) of modifiers
const rightBottom = rContour.bottom;
const leftBottom = lContour.bottom;
if (rightBottom <= leftBottom) {
rContour = nextRContour(rContour);
if (rContour) rSumMods += rContour.relX;
}
if (rightBottom >= leftBottom) {
lContour = nextLContour(lContour);
if (lContour) lSumMods += lContour.relX;
}
}
// Set threads and update extreme nodes. In the first case, the
// current subtree is taller than the left siblings.
if (!rContour && lContour) setLThr(w, i, lContour, lSumMods);
// In the next case, the left siblings are taller than the current subtree
else if (rContour && !lContour) setRThr(w, i, rContour, rSumMods);
};
/* eslint-enable complexity */
// Move subtree by changing relX.
const moveSubtree = (subtree, distance) => {
subtree.relX += distance;
subtree.lExtRelX += distance;
subtree.rExtRelX += distance;
};
const distributeExtra = (w, curSubtreeI, leftSibI, dist) => {
const curSubtree = w.children[curSubtreeI];
const n = curSubtreeI - leftSibI;
// Are there intermediate children?
if (n > 1) {
const delta = dist / n;
w.children[leftSibI + 1].shift += delta;
curSubtree.shift -= delta;
curSubtree.change -= dist - delta;
}
};
const nextLContour = w => {
return w.hasChildren ? w.firstChild : w.lThr;
};
const nextRContour = w => {
return w.hasChildren ? w.lastChild : w.rThr;
};
const setLThr = (w, i, lContour, lSumMods) => {
const firstChild = w.firstChild;
const lExt = firstChild.lExt;
const curSubtree = w.children[i];
lExt.lThr = lContour;
// Change relX so that the sum of modifier after following thread is correct.
const diff = lSumMods - lContour.relX - firstChild.lExtRelX;
lExt.relX += diff;
// Change preliminary x coordinate so that the node does not move.
lExt.prelim -= diff;
// Update extreme node and its sum of modifiers.
firstChild.lExt = curSubtree.lExt;
firstChild.lExtRelX = curSubtree.lExtRelX;
};
// Mirror image of setLThr.
const setRThr = (w, i, rContour, rSumMods) => {
const curSubtree = w.children[i];
const rExt = curSubtree.rExt;
const lSib = w.children[i - 1];
rExt.rThr = rContour;
const diff = rSumMods - rContour.relX - curSubtree.rExtRelX;
rExt.relX += diff;
rExt.prelim -= diff;
curSubtree.rExt = lSib.rExt;
curSubtree.rExtRelX = lSib.rExtRelX;
};
// Position root between children, taking into account their modifiers
const positionRoot = w => {
if (w.hasChildren) {
const k0 = w.firstChild;
const kf = w.lastChild;
const prelim = (k0.prelim + k0.relX - k0.xSize / 2 +
kf.relX + kf.prelim + kf.xSize / 2 ) / 2;
Object.assign(w, {
prelim,
lExt: k0.lExt, lExtRelX: k0.lExtRelX,
rExt: kf.rExt, rExtRelX: kf.rExtRelX,
});
}
};
// Make/maintain a linked list of the indexes of left siblings and their
// lowest vertical coordinate.
const updateLows = (lowY, index, lastLows) => {
// Remove siblings that are hidden by the new subtree.
while (lastLows !== null && lowY >= lastLows.lowY)
lastLows = lastLows.next;
// Prepend the new subtree.
return {
lowY,
index,
next: lastLows,
};
};
function walkTree(tree, callback, key = 'c') {
const walk = (item, parent) => callback(item, () => {
var _item$key;
(_item$key = item[key]) == null ? void 0 : _item$key.forEach(child => {
walk(child, item);
});
}, parent);
walk(tree);
}
let canvas;
function getTextRect(items, font) {
// re-use canvas object for better performance
if (!canvas) canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
context.font = font;
let maxWidth = 0;
let width = 0;
let row = 0;
const walk = item => {
if (item.t === 'text') {
item.p = { ...item.p
};
if (!width && row) item.p.newline = true;
const metrics = context.measureText(item.v);
width += metrics.width;
if (maxWidth < width) maxWidth = width;
} else if (item.t === 'softbreak') {
width = 0;
row += 1;
} else if (item.t === 'link') {
item.c.forEach(walk);
}
};
items.forEach(walk);
return [maxWidth, row + 1];
}
function linkWidth(nodeData) {
const data = nodeData.data;
return Math.max(6 - 2 * data.d, 1.5);
}
function getKey(v) {
const result = ['<'];
v.forEach(item => {
if (item.t === 'text') result.push(item.v.replace(/[<|&]/g, m => `&${m}`));else if (item.c) result.push(getKey(item.c));
});
result.push('>');
return result.join('');
}
function addSpacing(tree, spacing) {
let depth = 0;
walkTree(tree, (item, next) => {
item.y += depth * spacing;
depth += 1;
next();
depth -= 1;
}, 'children');
}
function getChildNodes() {
return this.childNodes;
}
function markmap(svg, data, opts) {
svg = svg.datum ? svg : d3.select(svg);
const classList = (svg.attr('class') || '').split(' ').filter(Boolean);
if (classList.indexOf('markmap') < 0) {
classList.push('markmap');
svg.attr('class', classList.join(' '));
}
const style = svg.append('style');
const g = svg.append('g');
const zoom = d3.zoom().on('zoom', handleZoom);
const svgNode = svg.node();
const options = {
duration: 500,
nodeFont: '300 16px sans-serif',
lineHeight: 20,
spacingVertical: 5,
spacingHorizontal: 80,
autoFit: false,
fitRatio: 0.95,
color: d3.scaleOrdinal(d3.schemeCategory10),
colorDepth: 0,
...opts
};
const state = {};
updateStyle();
if (data) {
setData(data);
fit(); // always fit for the first render
}
svg.call(zoom);
return {
setData,
setOptions,
fit
};
function updateStyle() {
style.text(`\
.markmap a { fill: #0097e6; }
.markmap a:hover { fill: #00a8ff; }
.markmap path { fill: none; }
.markmap text { font: ${options.nodeFont} }
.markmap tspan.markmap-em { font-style: italic; }
.markmap tspan.markmap-strong { font-weight: 500; }
.markmap g > g { cursor: pointer; }
`);
}
function handleZoom() {
const {
transform
} = d3.event;
g.attr('transform', transform);
}
function addKeys(node) {
let i = 1;
const {
colorDepth
} = options;
walkTree(node, (item, next, parent) => {
var _item$v;
options.color(`${i}`); // preload colors
item.p = {
i,
...item.p
};
if ((_item$v = item.v) == null ? void 0 : _item$v.length) {
var _parent$p;
item.p.k = ((parent == null ? void 0 : (_parent$p = parent.p) == null ? void 0 : _parent$p.k) || '') + getKey(item.v);
}
next();
if (!colorDepth || item.d === colorDepth) i += 1;
});
}
function setOptions(opts) {
Object.assign(options, opts);
}
function setData(data, opts) {
addKeys(data);
state.data = data;
if (opts) setOptions(opts);
renderData(data);
}
function fit() {
const {
width: offsetWidth,
height: offsetHeight
} = svgNode.getBoundingClientRect();
const {
minX,
maxX,
minY,
maxY
} = state;
const naturalWidth = maxY - minY;
const naturalHeight = maxX - minX;
const scale = Math.min(offsetWidth / naturalWidth * options.fitRatio, offsetHeight / naturalHeight * options.fitRatio, 2);
const initialZoom = d3.zoomIdentity.translate((offsetWidth - naturalWidth * scale) / 2 - minY * scale, (offsetHeight - naturalHeight * scale) / 2 - minX * scale).scale(scale);
svg.transition().duration(options.duration).call(zoom.transform, initialZoom);
}
function handleClick(d) {
var _data$p;
const {
data
} = d;
data.p = { ...data.p,
f: !((_data$p = data.p) == null ? void 0 : _data$p.f)
};
renderData(d.data);
}
function handleLink(d) {
d3.event.preventDefault();
window.open(d.p.href);
}
function renderTextNode(t, d) {
if (d.t === 'link') {
const a = t.append('a').attr('href', d.p.href).attr('title', d.p.title).on('click', handleLink);
const text = a.selectAll(getChildNodes).data(d => d.c);
text.enter().each(function (d) {
const t = d3.select(this);
renderTextNode(t, d);
});
}
if (d.t === 'text') {
t.append('tspan').text(d.v).attr('class', d => {
var _d$p;
const style = ((_d$p = d.p) == null ? void 0 : _d$p.style) || {};
return [style.em && 'markmap-em', style.strong && 'markmap-strong'].filter(Boolean).join(' ');
}).attr('x', d => {
var _d$p2;
return ((_d$p2 = d.p) == null ? void 0 : _d$p2.newline) ? 8 : null;
}).attr('dy', d => {
var _d$p3;
return ((_d$p3 = d.p) == null ? void 0 : _d$p3.newline) ? options.lineHeight : null;
});
}
}
function renderText(text) {
const textNode = text.selectAll(getChildNodes).data(d => d.data.v);
textNode.enter().each(function (d) {
const t = d3.select(this);
renderTextNode(t, d);
});
return text;
}
function renderData(originData) {
var _origin$data$x, _origin$data$y;
if (!state.data) return;
const layout = flextree().children(d => {
var _d$p4;
return !((_d$p4 = d.p) == null ? void 0 : _d$p4.f) && d.c;
}).nodeSize(d => {
const [width, rows] = getTextRect(d.data.v, options.nodeFont);
return [rows * options.lineHeight, width + 16];
}).spacing((a, b) => {
return a.parent === b.parent ? options.spacingVertical : options.spacingVertical * 2;
});
const tree = layout.hierarchy(state.data);
layout(tree);
addSpacing(tree, options.spacingHorizontal);
const descendants = tree.descendants().reverse();
const links = tree.links();
const linkShape = d3.linkHorizontal();
const minX = d3.min(descendants, d => d.x - d.xSize / 2);
const maxX = d3.max(descendants, d => d.x + d.xSize / 2);
const minY = d3.min(descendants, d => d.y);
const maxY = d3.max(descendants, d => d.y + d.ySize);
state.minX = minX;
state.maxX = maxX;
state.minY = minY;
state.maxY = maxY;
if (options.autoFit) fit();
const origin = originData ? descendants.find(item => item.data === originData) : tree;
const x0 = (_origin$data$x = origin.data.x0) != null ? _origin$data$x : origin.x;
const y0 = (_origin$data$y = origin.data.y0) != null ? _origin$data$y : origin.y; // Update the nodes
const node = g.selectAll('g').data(descendants, d => d.data.p.k);
const nodeEnter = node.enter().append('g').attr('transform', d => `translate(${y0 + origin.ySize - d.ySize},${x0 + origin.xSize / 2 - d.xSize})`).on('click', handleClick);
const nodeExit = node.exit().transition().duration(options.duration);
nodeExit.select('rect').attr('width', 0).attr('x', d => d.ySize);
nodeExit.select('text').attr('fill-opacity', 0);
nodeExit.attr('transform', d => `translate(${origin.y + origin.ySize - d.ySize},${origin.x + origin.xSize / 2 - d.xSize})`).remove();
const nodeMerge = node.merge(nodeEnter);
nodeMerge.transition().duration(options.duration).attr('transform', d => `translate(${d.y},${d.x - d.xSize / 2})`);
nodeMerge.selectAll('rect').data(d => [d], d => d.data.p.k).join(enter => {
return enter.append('rect').attr('x', d => d.ySize).attr('y', d => d.xSize - linkWidth(d) / 2).attr('width', 0).attr('height', linkWidth);
}, update => update, exit => exit.remove()).transition().duration(options.duration).attr('x', -1).attr('width', d => d.ySize + 2).attr('fill', d => options.color(d.data.p.i));
nodeMerge.selectAll('circle').data(d => d.data.c ? [d] : [], d => d.data.p.k).join(enter => {
return enter.append('circle').attr('stroke-width', '1.5').attr('cx', d => d.ySize).attr('cy', d => d.xSize).attr('r', 0);
}, update => update, exit => exit.remove()).transition().duration(options.duration).attr('r', 6).attr('stroke', d => options.color(d.data.p.i)).attr('fill', d => {
var _d$data$p;
return ((_d$data$p = d.data.p) == null ? void 0 : _d$data$p.f) ? options.color(d.data.p.i) : '#fff';
});
nodeMerge.selectAll('text').data(d => [d], d => d.data.p.k).join(enter => {
return enter.append('text').attr('x', 8).attr('y', options.lineHeight - 4).attr('text-anchor', 'start').attr('fill-opacity', 0).call(renderText);
}, update => update, exit => exit.remove()).transition().duration(options.duration).attr('fill-opacity', 1); // Update the links
g.selectAll('path').data(links, d => d.target.data.p.k).join(enter => {
const source = [y0 + origin.ySize, x0 + origin.xSize / 2];
return enter.insert('path', 'g').attr('d', linkShape({
source,
target: source
}));
}, update => update, exit => {
const source = [origin.y + origin.ySize, origin.x + origin.xSize / 2];
return exit.transition().duration(options.duration).attr('d', linkShape({
source,
target: source
})).remove();
}).transition().duration(options.duration).attr('stroke', d => options.color(d.target.data.p.i)).attr('stroke-width', d => linkWidth(d.target)).attr('d', d => {
const source = [d.source.y + d.source.ySize, d.source.x + d.source.xSize / 2];
const target = [d.target.y, d.target.x + d.target.xSize / 2];
return linkShape({
source,
target
});
});
descendants.forEach(d => {
d.data.x0 = d.x;
d.data.y0 = d.y;
});
}
}
exports.markmap = markmap;
}(this.markmap = this.markmap || {}, d3));

View File

@ -50,6 +50,7 @@
.project-item{
/*float: left;*/
min-width: 0;
width: 350px;
height: 170px;
/*margin-top: 20px;*/
@ -167,7 +168,7 @@ body, html {
min-width: 0;
}
.project-title{
font-size: 24px;
font-size: 20px;
font-weight: 700;
margin:20px;
text-align: center;
@ -369,6 +370,7 @@ li.active > a,li.active > div > a{
@media screen and (max-width:768px){
/* 首页 */
.project-item{
min-width: 0;
width: 100%;
height: 170px;
margin:10px;

View File

@ -1,3 +1,4 @@
// ****生成可编辑HTML表格****
function InsertLine(obj) {
var table_id = "#" + $(obj).attr("name");
var name = $(obj).attr("name");

View File

@ -1,142 +0,0 @@
/* PrismJS 1.16.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+apacheconf+apl+applescript+c+arff+asciidoc+asm6502+csharp+autohotkey+autoit+bash+basic+batch+bison+bnf+brainfuck+bro+cpp+aspnet+arduino+cil+coffeescript+cmake+clojure+ruby+csp+css-extras+d+dart+diff+markup-templating+dns-zone-file+docker+ebnf+eiffel+ejs+elixir+elm+erb+erlang+fsharp+flow+fortran+gcode+gedcom+gherkin+git+glsl+gml+go+graphql+groovy+less+handlebars+haskell+haxe+hcl+http+hpkp+hsts+ichigojam+icon+inform7+ini+io+j+java+scala+php+javastacktrace+jolie+jq+javadoclike+n4js+json+jsonp+json5+julia+keyman+kotlin+latex+markdown+liquid+lisp+livescript+lolcode+lua+makefile+crystal+django+matlab+mel+mizar+monkey+n1ql+typescript+nand2tetris-hdl+nasm+nginx+nim+nix+nsis+objectivec+ocaml+opencl+oz+parigp+parser+pascal+pascaligo+pcaxis+perl+jsdoc+phpdoc+php-extras+sql+powershell+processing+prolog+properties+protobuf+scss+puppet+pure+python+q+qore+r+js-extras+jsx+renpy+reason+vala+rest+rip+roboconf+textile+rust+sas+sass+stylus+javadoc+scheme+shell-session+smalltalk+smarty+plsql+soy+twig+swift+yaml+tcl+haml+toml+tt2+pug+tsx+t4-templating+visual-basic+t4-cs+regex+vbnet+velocity+verilog+vhdl+vim+t4-vb+wasm+wiki+xeora+xojo+xquery+tap */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 1em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

File diff suppressed because one or more lines are too long

View File

@ -101,10 +101,12 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'doc_id':doc_id,
}
$.post("{% url 'del_doc' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
//修改成功
window.location.reload();

View File

@ -88,10 +88,12 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'doctemp_id':doctemp_id,
}
$.post("{% url 'del_doctemp' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
//修改成功
window.location.reload();

View File

@ -118,11 +118,13 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'pname':$("#pname").val(),
'desc':$("#desc").val(),
}
$.post("{% url 'create_project' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
//创建成功
window.location.reload();

View File

@ -108,11 +108,13 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'types':1,
'all_cnt':$("#regCodeCnt").val(),
}
$.post("{% url 'register_code_manage' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
//新增成功,刷新页面
window.location.reload();

View File

@ -61,7 +61,9 @@
var layer = layui.layer;
//获取用户列表
getUserInfo = function () {
layer.load(1);
$.post("{% url 'user_manage' %}",{'username':$("#username").val()},function(r){
layer.closeAll('loading');
if(r.status){
//执行表格渲染
table.render({
@ -94,12 +96,14 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'username':$("#user").val(),
'password':$("#password").val(),
'email':$("#email").val()
}
$.post("{% url 'create_user' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
//创建成功,刷新页面
window.location.reload();
@ -122,12 +126,14 @@
content:'<div style="padding:10px 0 0 20px;">修改用户[' + username + ']的密码:</div><div style="padding: 20px;"><input class="layui-input" type="password" id="newPwd1" style="margin-bottom:10px;" placeholder="输入新密码" required lay-verify="required"><input class="layui-input" type="password" id="newPwd2" placeholder="再次确认新密码" required lay-verify="required"></div>',
btn:['确认修改','取消'],
yes:function (index,layero) {
layer.load();
data = {
'user_id':uid,
'password':$("#newPwd1").val(),
'password2':$("#newPwd2").val(),
}
$.post("{% url 'change_pwd' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
//修改成功
window.location.reload();
@ -152,10 +158,12 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'user_id':uid,
}
$.post("{% url 'del_user' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
//删除成功
window.location.reload();

View File

@ -47,7 +47,9 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
$.post("{% url 'manage_token' %}",function(r){
layer.closeAll('loading');
if(r.status){
//生成成功
//window.location.reload();

View File

@ -139,7 +139,13 @@
<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 'mindmap/d3@5.js' %}"></script>
<script src="{% static 'mindmap/transform.js' %}"></script>
<script src="{% static 'mindmap/view.js' %}"></script>
<!-- 脑图结束 -->
<script src="{% static 'editor.md/editormd.js' %}"></script>
<!-- <script src="{% static 'editor.md/editormd.min.js' %}"></script> -->
<script src="{% static 'mrdoc.editor.js' %}"></script>
<script src="{% static 'mrdoc.js' %}?version={{mrdoc_version}}"></script>
<script>
@ -208,10 +214,10 @@
toolbarIcons : function() {
return [
"undo", "redo", "|",
"bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase","kaiSpan", "|",
"bold", "del", "italic", "quote","kaiSpan", "|",
"h1", "h2", "h3", "h4", "|",
"list-ul", "list-ol", "hr", "link", "reference-link", "|",
"imgUpload", "attachment" ,"code", "code-block", "htmltable", "datetime", "emoji", "html-entities", "pagebreak", "|",
"mindmap","imgUpload", "attachment" ,"code", "code-block", "htmltable", "|","datetime", "emoji", "html-entities", "pagebreak", "|",
"watch", "preview", "|",
"help"
]
@ -225,6 +231,7 @@
imgUpload:'fa-image',
attachment:'fa-upload',
htmltable:'fa-table',
mindmap:'fa-sitemap',
},
//设置自定义工具栏按钮的事件
toolbarHandlers : {
@ -272,7 +279,7 @@
id:'uploadAttach',//配置ID,
content:$('#upload-attach'),
success: function(layero, index){
layer.load();
layer.load(1);
$.post('{% url "manage_attachment" %}',{types:2},function(r){
$("#attach_table tbody").empty()
if(r.status){
@ -311,21 +318,44 @@
type:1,
id:'addTable',
title:'添加表格',
//content:"<div style='margin:10px;'><input type='number' id='row' class='layui-input' placeholder='输入行数'><input type='number' id='col' class='layui-input' placeholder='输入列数'></div>",
area:['600px','400px'],
content:$("#layer-table"),
btn:['确定','取消'], //添加按钮
yes:function(index,layero){
var table_md = convertTable("DataTable1")
console.log(table_md)
editor.insertValue("\n" + table_md);
var current_tab_id = $("#insert-table-div .layui-tab-title .layui-this").attr("lay-id");
console.log(current_tab_id)
//粘贴的表格
if(current_tab_id == 'pasteTable'){
// console.log("插入粘贴的表格")
editor.insertValue("\n" + $("#pasteExcel").val())
layer.close(index)
}else{//创建编辑的表格
try {
var table_md = convertTable("DataTable1")
// console.log(table_md)
editor.insertValue("\n" + table_md);
layer.close(index)
} catch (error) {
layer.msg("请生成表格")
}
}
$("#TableGroup").empty();//清空表格
layer.close(index)
$("#pasteExcel").val('');
},
btn2:function(index,layero){
$("#TableGroup").empty();//清空表格
$("#pasteExcel").val('');
layer.close(index) // 关闭弹出框
}
});
},
mindmap:function(cm,icon,cursor,selection){
cm.replaceSelection('```mindmap\n' + selection + "\n```");
if(selection === "") {
cm.setCursor(cursor.line+1, cursor.ch);
}
}
},
//设置语言
@ -335,6 +365,7 @@
imgUpload:"添加图片到文档",
attachment:"添加附件",
htmltable:"添加表格",
mindmap:"添加思维导图",
}
},
//配置项
@ -358,6 +389,9 @@
onchange:function(){
md_changed = true
},
onload : function() {
// this.insertValue(" ")
}
});
//粘贴上传图片
$("#editor-md").on('paste', function (ev) {
@ -371,7 +405,7 @@
reader.onload = function (event) {
var base64 = event.target.result;
//ajax上传图片
layer.load();
layer.load(1);
$.post("{% url 'upload_doc_img' %}",{base:base64}, function (ret) {
layer.msg(ret.message);
if (ret.success === 1) {
@ -388,6 +422,7 @@
}
}
});
//未保存离开提示
window.onbeforeunload =function() {
   if(md_changed){
@ -403,7 +438,7 @@
//console.log(data.index); //得到当前Tab的所在下标
//console.log(data.elem); //得到当前的Tab大容器
if(data.index == 1){
layer.load();
layer.load(1);
console.log('选择图片')
$("#select-img-group").empty(); //删除已有分组按钮
//请求新的分组数据
@ -451,7 +486,7 @@
};
//切换图片分组
switchImgGroup = function(e){
layer.load();
layer.load(1);
$.post("{% url 'manage_image' %}", {
'types': 2,
'group_id': e
@ -489,7 +524,6 @@
layer.closeAll();
}
</script>
<script src="{% static 'mrdoc.js' %}?version={{mrdoc_version}}"></script>
{% block custom_script %}
{% endblock %}
</body>
@ -536,23 +570,37 @@
</div>
<!-- 添加表格div -->
<div id="layer-table" style="display: none;margin: 10px;">
<div class="layui-row" style="margin: 10px;">
<div class="layui-form-item">
<div class="layui-inline">
<div class="layui-input-inline" style="width: 50px;">
<input type="number" placeholder="行" id="row" class="layui-input" value="3">
<div class="layui-tab" lay-filter="table-tab" id="insert-table-div">
<ul class="layui-tab-title">
<li class="layui-this" lay-id="generaTable">生成表格</li>
<li lay-id="pasteTable">粘贴表格</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show" >
<div class="layui-row" style="margin: 10px;">
<div class="layui-form-item">
<div class="layui-inline">
<div class="layui-input-inline" style="width: 50px;">
<input type="number" placeholder="行" id="row" class="layui-input" value="3">
</div>
<div class="layui-form-mid">x</div>
<div class="layui-input-inline" style="width: 50px;">
<input type="number" placeholder="列" id="col" class="layui-input" value="3">
</div>
<div class="layui-form-mid" style="width: 100px;">
<button class="layui-btn layui-btn-normal layui-btn-xs" onclick="addtable(1)" >生成表格</button>
</div>
</div>
</div>
</div>
<div class="layui-form-mid">x</div>
<div class="layui-input-inline" style="width: 50px;">
<input type="number" placeholder="列" id="col" class="layui-input" value="3">
</div>
<div class="layui-form-mid" style="width: 100px;">
<button class="layui-btn layui-btn-normal layui-btn-xs" onclick="addtable(1)" >生成表格</button>
</div>
</div>
<div class="layui-row" style="margin:10px;" id="TableGroup"></div>
</div>
<div class="layui-tab-item" >
<textarea placeholder="粘贴Excel或在线电子表格的内容" class="layui-textarea" style="height: 200px;" id="pasteExcel"></textarea>
</div>
</div>
</div>
<div class="layui-row" style="margin:10px;" id="TableGroup"></div>
</div>
<script>
//按钮选择上传图片
@ -561,7 +609,7 @@
elem: '#upload_img',
url: '{% url "upload_doc_img" %}',
before: function(obj){ //obj参数包含的信息跟 choose回调完全一致可参见上文。
layer.load(); //上传loading
layer.load(1); //上传loading
},
done: function(res, index, upload){ //上传后的回调
//上传成功
@ -587,7 +635,7 @@
url: '{% url "manage_attachment" %}',
data:{types:0,csrfmiddlewaretoken: '{{ csrf_token }}'},
before: function(obj){ //obj参数包含的信息跟 choose回调完全一致可参见上文。
layer.load(); //上传loading
layer.load(1); //上传loading
},
done: function(res, index, upload){ //上传后的回调
//上传成功,刷新页面
@ -608,5 +656,79 @@
$('div.editormd-preview').on('click','a',function(e){
e.target.target = '_blank';
});
// 粘贴表格文本框侦听paste粘贴事件
// 列宽的函数
function columnWidth(rows, columnIndex) {
return Math.max.apply(null, rows.map(function(row) {
return row[columnIndex].length
}))
};
// 检查是否是个表格
function looksLikeTable(data) {
return true
};
// 编辑器侦听paste粘贴事件
var pasteExcel = document.getElementById('pasteExcel')
pasteExcel.addEventListener("paste", function(event) {
console.log('粘贴Excel')
var clipboard = event.clipboardData
var data = clipboard.getData('text/plain')
data = data.replace(/(?:[\n\u0085\u2028\u2029]|\r\n?)$/, '');
if(looksLikeTable(data)) {
event.preventDefault()
}else{
return
}
// 行
var rows = data.split((/[\n\u0085\u2028\u2029]|\r\n?/g)).map(function(row) {
console.log(row)
return row.split("\t")
})
// 列对齐
var colAlignments = []
// 列宽
var columnWidths = rows[0].map(function(column, columnIndex) {
var alignment = "l"
var re = /^(\^[lcr])/i
var m = column.match(re)
if (m) {
var align = m[1][1].toLowerCase()
if (align === "c") {
alignment = "c"
} else if (align === "r") {
alignment = "r"
}
}
colAlignments.push(alignment)
column = column.replace(re, "")
rows[0][columnIndex] = column
return columnWidth(rows, columnIndex)
})
var markdownRows = rows.map(function(row, rowIndex) {
return "| " + row.map(function(column, index) {
return column + Array(columnWidths[index] - column.length + 1).join(" ")
}).join(" | ") + " |"
row.map
})
markdownRows.splice(1, 0, "|" + columnWidths.map(function(width, index) {
var prefix = ""
var postfix = ""
var adjust = 0
var alignment = colAlignments[index]
if (alignment === "r") {
postfix = ":"
adjust = 1
} else if (alignment == "c") {
prefix = ":"
postfix = ":"
adjust = 2
}
return prefix + Array(columnWidths[index] + 3 - adjust).join("-") + postfix
}).join("|") + "|")
event.target.value = markdownRows.join("\n")
return false
});
</script>
</html>

View File

@ -160,7 +160,7 @@
,none: '文集暂无文档' //数据为空时的提示文本
},
click: function(obj){
console.log(obj.data); //得到当前点击的节点数据
//console.log(obj.data); //得到当前点击的节点数据
// console.log(obj.state); //得到当前节点的展开状态open、close、normal
// console.log(obj.elem); //得到当前节点元素
if(obj.data.level != 3){
@ -169,6 +169,7 @@
var $me = $(this)
if($me.data('id') == obj.data.id){
// console.log('点击了')
layer.msg("选择了上级文档:"+obj.data.title)
$me.find('span.layui-tree-txt').first().addClass('selected-parent-doc')
}else{
$me.find('span.layui-tree-txt').first().removeClass('selected-parent-doc')

View File

@ -188,12 +188,12 @@
// 页面加载执行
window.onload = function() {
value = document.getElementById('doc').innerHTML;
orig2 = document.getElementById('history').innerHTML;
initUI();
var d = document.createElement("div");
d.style.cssText = "width: 50px; margin: 7px; height: 14px";
dv.editor().addLineWidget(57, d)
value = document.getElementById('doc').innerHTML;
orig2 = document.getElementById('history').innerHTML;
initUI();
var d = document.createElement("div");
d.style.cssText = "width: 50px; margin: 7px; height: 14px";
dv.editor().addLineWidget(57, d)
};
function mergeViewHeight(mergeView) {

View File

@ -13,7 +13,6 @@
<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 'prism/prism.css' %}" />-->
<link rel="stylesheet" href="{% static 'katex/katex.min.css' %}?version={{mrdoc_version}}" />
<link rel="stylesheet" href="{% static 'share.js/css/share.min.css' %}?version={{mrdoc_version}}" />
<link rel="icon" href="{% static 'favicon_16.png' %}"/>
@ -198,7 +197,7 @@
</div>
<!-- 标题结束 -->
<!-- 正文开始 -->
<div class="markdown-body" id="content" style="padding: 0 20px;">
<div class="markdown-body" id="content" style="padding: 20px;">
{% block page_content %}
{% endblock %}
</div>
@ -227,7 +226,15 @@
<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 'mindmap/d3@5.js' %}"></script>
<script src="{% static 'mindmap/transform.js' %}"></script>
<script src="{% static 'mindmap/view.js' %}"></script>
<!-- <script src="{% static 'mindmap/mindmap.js' %}"></script> -->
<!-- 脑图结束 -->
<!-- <script src="{% static 'editor.md/editormd.min.js' %}"></script> -->
<script src="{% static 'editor.md/editormd.js' %}"></script>
<script src="{% static 'share.js/js/social-share.min.js' %}"></script>
<script>
//加载页面时执行一次

View File

@ -1,4 +1,4 @@
<div class="layui-footer" style="border-top: 1px #e6e6e6 solid;text-align:center;margin:20px;width:100%;">
<div class="layui-footer" style="border-top: 1px #e6e6e6 solid;text-align:center;margin-top:20px;width:100%;">
<div style="margin-top:10px;">
© <a href="/">MrDoc 2019-2020</a>&nbsp;|&nbsp;
<span class="layui-hide-xs">基于<a href="https://www.djangoproject.com/" target="_blank">Django</a>&nbsp;|&nbsp;</span>

View File

@ -82,7 +82,11 @@
elem: '#upload_attachment',
url: '{% url "manage_attachment" %}',
data:{types:0,csrfmiddlewaretoken: '{{ csrf_token }}'},
before: function(obj){ //obj参数包含的信息跟 choose回调完全一致可参见上文。
layer.load(1); //上传loading
},
done: function(res, index, upload){ //上传后的回调
layer.closeAll('loading'); //关闭loading
//上传成功,刷新页面
if(res.status == 1){
window.location.reload();
@ -107,18 +111,20 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'attach_id':attach_id,
'types':1
}
$.post("{% url 'manage_attachment' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
//修改成功
window.location.reload();
//layer.close(index)
}else{
//修改失败,提示
console.log(r)
// console.log(r)
layer.msg(r.data)
}
})

View File

@ -106,10 +106,12 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'doc_id':doc_id,
}
$.post("{% url 'del_doc' %}",data,function(r){
layer.closeAll('loading')
if(r.status){
//修改成功
window.location.reload();

View File

@ -86,13 +86,13 @@
'history_id':history_id,
}
$.post("{% url 'manage_doc_history' doc.id %}",data,function(r){
layer.closeAll('loading')
if(r.status){
//修改成功
window.location.reload();
//layer.close(index)
}else{
//修改失败,提示
layer.close('loading')
layer.msg(r.data)
}
})

View File

@ -86,17 +86,19 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'doctemp_id':doctemp_id,
}
$.post("{% url 'del_doctemp' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
//修改成功
window.location.reload();
//layer.close(index)
}else{
//修改失败,提示
console.log(r)
// console.log(r)
layer.msg(r.data)
}
})

View File

@ -99,18 +99,20 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'img_id':img_id,
'types':0
}
$.post("{% url 'manage_image' %}",data,function(r){
layer.closeAll('loading')
if(r.status){
//删除成功
window.location.reload();
//layer.close(index)
}else{
//删除失败,提示
console.log(r)
// console.log(r)
layer.msg(r.data)
}
})
@ -131,6 +133,7 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'types':1,
'img_id':img_id,
@ -138,13 +141,14 @@
}
console.log(data)
$.post("{% url 'manage_image' %}",data,function(r){
layer.closeAll('loading')
if(r.status){
//移动成功
window.location.reload();
//layer.close(index)
}else{
//移动失败,提示
console.log(r)
// console.log(r)
layer.msg(r.data)
}
})
@ -176,18 +180,19 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1); //上传loading
data = {
'types':0,
'group_name':$("#img_group_name").val(),
}
$.post("{% url 'manage_img_group' %}",data,function(r){
layer.closeAll('loading');
if(r.status){
//新建成功
window.location.reload();
//layer.close(index)
}else{
//新建失败,提示
console.log(r)
// console.log(r)
layer.msg(r.data)
}
})
@ -199,7 +204,11 @@
upload.render({
elem: '#upload_img',
url: '{% url "upload_doc_img" %}',
before: function(obj){ //obj参数包含的信息跟 choose回调完全一致可参见上文。
layer.load(); //上传loading
},
done: function(res, index, upload){ //上传后的回调
layer.closeAll('loading'); //关闭loading
//上传成功,刷新页面
if(res.success == 1){
window.location.reload();

View File

@ -67,11 +67,13 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'types':2,
'group_id':group_id,
}
$.post("{% url 'manage_img_group' %}",data,function(r){
layer.closeAll('loading')
if(r.status){
//删除成功
window.location.reload();
@ -95,19 +97,21 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'types':1,
'group_id':group_id,
'group_name':$("#new-group-name").val()
}
$.post("{% url 'manage_img_group' %}",data,function(r){
layer.closeAll('loading')
if(r.status){
//修改成功
window.location.reload();
//layer.close(index)
}else{
//修改失败,提示
console.log(r)
// console.log(r)
layer.msg(r.data)
}
})

View File

@ -128,11 +128,13 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'pname':$("#pname").val(),
'desc':$("#desc").val(),
}
$.post("{% url 'create_project' %}",data,function(r){
layer.closeAll('loading'); //关闭loading
if(r.status){
//创建成功
window.location.reload();
@ -157,19 +159,21 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'pro_id':pro_id,
'name':$("#pname").val(),
'desc':$("#desc").val(),
}
$.post("{% url 'modify_project' %}",data,function(r){
layer.closeAll('loading'); //关闭loading
if(r.status){
//修改成功
window.location.reload();
//layer.close(index)
}else{
//修改失败,提示
console.log(r)
// console.log(r)
layer.msg(r.data)
}
})
@ -187,10 +191,12 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'pro_id':pro_id,
}
$.post("{% url 'del_project' %}",data,function(r){
layer.closeAll('loading'); //关闭loading
if(r.status){
//修改成功
window.location.reload();
@ -220,14 +226,14 @@
'project_id':pro_id,
}
$.post("{% url 'report_md' %}",data,function(r){
layer.closeAll('loading'); //关闭loading
if(r.status){
//导出成功
layer.close(index)
//文件下载提示
downloadMd(r.data)
}else{
//导出失败,提示
console.log(r)
// console.log(r)
layer.msg(r.data)
}
layer.close(load)

View File

@ -98,19 +98,21 @@
form.render('radio'); // 刷新checkbox开关渲染(否则开关按钮会不显示)
},
yes:function (index,layero) {
layer.load(1);
data = {
'types':0,
'username':$("#username").val(),
'role': $('input[name="add-role"]:checked').val()
}
$.post("{% url 'manage_pro_colla' pro.id %}",data,function(r){
layer.closeAll('loading'); //关闭loading
if(r.status){
//添加成功
window.location.reload();
//layer.close(index)
}else{
//添加失败,提示
console.log(r)
// console.log(r)
layer.msg(r.data)
}
})

View File

@ -117,7 +117,7 @@
<script>
//获取文档数和上级文档信息
$(function(){
layer.load();
layer.load(1);
var doc_parent_id = {{ doc.parent_doc }};
$.post("{% url 'get_pro_doc_tree' %}",{'pro_id':$("#project").val()},function(r){
if(r.status){
@ -167,7 +167,7 @@
});
//发布文档
createDoc = function(){
layer.load();
layer.load(1);
var data = {
'doc_id':{{ doc.id }},
'project':$("#project").val(),
@ -179,15 +179,14 @@
'sort':$("#sort").val(),
}
$.post("{% url 'modify_doc' doc_id=doc.id %}",data,function(r){
layer.closeAll("loading");
if(r.status){
layer.closeAll("loading");
//修改成功
layer.msg('发布成功,即将跳转',function(){
md_changed = false;
window.location.href = "{% url 'doc' pro_id=doc.top_doc doc_id=doc.id %}";
});
}else{
layer.closeAll("loading");
//修改失败
layer.msg('保存失败');
}
@ -195,7 +194,7 @@
};
//保存草稿
saveDoc = function(){
layer.load();
layer.load(1);
var data = {
'doc_id':{{ doc.id }},
'project':$("#project").val(),
@ -207,15 +206,14 @@
'status':0
}
$.post("{% url 'modify_doc' doc_id=doc.id %}",data,function(r){
layer.closeAll("loading");
if(r.status){
//修改成功
layer.closeAll("loading");
layer.msg('保存成功',function(){
md_changed = false;
window.location.href = "{% url 'modify_doc' doc.id %}";
});
}else{
layer.closeAll("loading");
//修改失败
layer.msg('保存失败');
}
@ -257,11 +255,10 @@
var url = "{% url 'diff_doc' 0 1 %}";
url = url.replace(0,doc_id).replace(1,history_id)
$.post(url,function(r){
layer.closeAll("loading");
if(r.status){
editor.setMarkdown(r.data);
layer.closeAll()
}else{
layer.closeAll("loading");
layer.msg(r.data)
}
});

View File

@ -59,23 +59,22 @@
<script>
//修改文档模板
modifyDocTemp = function(){
layer.load();
layer.load(1);
var data = {
'doctemp_id':{{ doctemp.id }},
'name':$("#doctemp-name").val(),
'content':editor.getMarkdown(),
}
$.post("{% url 'modify_doctemp' doctemp_id=doctemp.id %}",data,function(r){
layer.closeAll("loading");
if(r.status){
//修改成功
layer.closeAll("loading");
layer.msg('修改成功,即将跳转',function(){
md_changed = false;
window.location.href = "{% url 'manage_doctemp' %}";
});
}else{
//创建失败
layer.closeAll("loading");
layer.msg('保存失败');
}
});

View File

@ -25,6 +25,49 @@
.layui-laypage .layui-laypage-curr .layui-laypage-em{
background-color: #333 !important;
}
/* 控制栏表单下拉框样式 */
.index-control .layui-input-inline{
width: 100px;
}
.index-control .layui-input{
height: 25px;
border: none;
}
.index-control .layui-form-select dl {
top: 30px;
}
.index-control .layui-form-item{
margin-bottom: 0px;
}
/* 文集列表样式 */
.project-item-list{
/* float: left; */
min-width: 0;
width: 100vw;
height: 120px;
/* margin-top: 20px; */
/* margin-left: 20px; */
margin: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,.055);
}
/* 移动端筛选控制栏样式 */
@media screen and (max-width: 768px){
/* 控制栏样式 */
.index-control .layui-form-item .layui-inline{
display: -webkit-inline-box;
}
.index-control .layui-form-item .layui-input-inline{
display: -webkit-inline-box;
float: none;
left: -3px;
/* width: auto; */
margin: 0;
}
}
</style>
</head>
<body class="layui-container">
@ -32,8 +75,53 @@
{% include 'app_doc/head_base.html' %}
<!-- 页头结束 -->
<!-- 筛选栏 -->
<div class="layui-container" style="margin-top: 10px;">
<!-- 表单风格开始 -->
<div class="layui-row">
<form class="index-control layui-form" style="float: right;" lay-filter="filter-project-form">
<div class="layui-form-item">
<!-- 筛选开始 -->
<div class="layui-inline">
<div class="layui-input-inline">
<select name="role" lay-verify="" id="sel-role">
<option value="">筛选</option>
{% if request.user.is_authenticated %}
<option value="0">公开文集</option>
<option value="1">私密文集</option>
<option value="99">协作文集</option>
<option value="3">访问码文集</option>
{% else %}
<option value="0">公开文集</option>
<option value="3">访问码文集</option>
{% endif %}
</select>
</div>
<div class="layui-input-inline">
<select name="sort" lay-verify="" id="sel-sort">
<option value="">排序</option>
<option value="0" {% if sort == 0 %} checked {% endif %}>时间升序</option>
<option value="1" {% if sort == 1 %} checked {% endif %}>时间降序</option>
</select>
</div>
</div>
<!-- 筛选结束 -->
<!-- 切换开始 -->
<div class="layui-inline">
<div class="layui-input-inline" style="width: 40px;">
<a href="javascript:void(0);" id="switch-grid-list"><i class="layui-icon layui-icon-align-left"></i></a>
</div>
</div>
<!-- 切换结束 -->
</div>
</form>
</div>
<!-- 表单风格结束 -->
</div>
<!-- 搜索结果提示 -->
{% if kw != '' %}
<div style="text-align: center;margin-top: 5px;">符合“<span style="color: #FF5722;font-weight: 700;">{{kw}}</span>”的搜索结果:</div>
<div style="text-align: center;margin-top: 0px;">符合“<span style="color: #FF5722;font-weight: 700;">{{kw}}</span>”的搜索结果:</div>
{% endif %}
<!-- 主体 -->
@ -41,25 +129,25 @@
<!-- 遍历文集列表 -->
{% load project_filter %}
{% for p in projects %}
<!-- 网格布局 -->
<div class="layui-col-md3 layui-col-xs12 project-item">
<a href="{% url 'pro_index' p.id %}" title="{{p.name}}">
<div class="layui-card">
<div class="layui-card-header" style="overflow:hidden;white-space: nowrap;text-overflow: ellipsis;color: black;">
<div class="layui-card-header" >
{% if p.role == 1 %}
<strong><i class="layui-icon layui-icon-password"></i> {{ p.name }}</strong>
<p class="layui-elip" style="font-weight: 700;"><i class="layui-icon layui-icon-circle-dot" style="color: #FF5722;"></i> {{ p.name }}</p>
{% elif p.role == 2 %}
<strong><i class="layui-icon layui-icon-user"></i> {{ p.name }}</strong>
<p class="layui-elip" style="font-weight: 700;"><i class="layui-icon layui-icon-circle-dot" style="color: #009688;"></i> {{ p.name }}</p>
{% elif p.role == 3 %}
<strong><i class="layui-icon layui-icon-key"></i> {{ p.name }}</strong>
<p class="layui-elip" style="font-weight: 700;"><i class="layui-icon layui-icon-circle-dot" style="color: #1E9FFF;"></i> {{ p.name }}</p>
{% else %}
<strong>{{ p.name }}</strong>
<p class="layui-elip" style="font-weight: 700;"><i class="layui-icon layui-icon-circle-dot" style="color: #5FB878;"></i> {{ p.name }}</p>
{% endif %}
</div>
<div class="layui-card-body">
<p>作者:{{p.create_user}}</p>
<p style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis;">最新:<u title="{{p.id | get_new_doc}}">{{p.id | get_new_doc}}</u></p>
<p class="tooltip">简介:
<p class="layui-word-aux layui-elip">作者:{{p.create_user}}</p>
<p class="layui-word-aux">最新:{{p.id | get_new_doc}}</p>
<p class="tooltip layui-word-aux">简介:
{% if p.intro == "" %}
<span class="">此文集没有填写简介</span>
{% else %}
@ -67,11 +155,49 @@
<span class="tooltip-content clearfix">{{ p.intro }}</span>
{% endif %}
</p>
<p></p>
</div>
</div>
</a>
</div>
<!-- 网格布局结束 -->
<!-- 列表布局开始 -->
<div class="project-item-list layui-row layui-hide">
<a href="{% url 'pro_index' p.id %}" title="{{p.name}}">
<div class="layui-col-md1 layui-col-xs2" style="height: 100%;;text-align: center;display: flex;display: -webkit-flex;justify-content: center;align-items:center;">
{% if p.role == 1 %}
<i class="layui-icon layui-icon-menu-fill" style="font-size: 30px;color: #FF5722;"></i>
{% elif p.role == 2 %}
<i class="layui-icon layui-icon-group" style="font-size: 30px;color: #009688;"></i>
{% elif p.role == 3 %}
<i class="layui-icon layui-icon-key" style="font-size: 30px;color: #1E9FFF;"></i>
{% else %}
<i class="layui-icon layui-icon-read" style="font-size: 30px;color: #5FB878;"></i>
{% endif %}
</div>
<div class="layui-col-md11 layui-col-xs10">
<div class="layui-card">
<div class="layui-card-header layui-elip" >
<strong class="layui-elip">{{p.name}}</strong>
</div>
<div class="layui-card-body layui-word-aux layui-elip">
<p class="">
<i class="layui-icon layui-icon-user"></i> {{p.create_user}}
</p>
<p class="tooltip layui-word-aux">
{% if p.intro == "" %}
此文集没有填写简介
{% else %}
<!-- <span class="">{{ p.intro | slice:'30' }}…</span> -->
{{ p.intro }}
{% endif %}
</p>
</div>
</div>
</div>
</a>
</div>
<!-- 列表布局结束 -->
{% endfor %}
{% if project_list.count == 0 %}
<img src="{% static 'non_doc.png' %}">
@ -83,7 +209,7 @@
<div class="layui-box layui-laypage layui-laypage-default">
<!-- 上一页 -->
{% if projects.has_previous %}
<a href="?page={{ projects.previous_page_number }}&kw={{projects.kw}}" class="layui-btn layui-btn-xs layui-btn-normal">上一页</a>
<a href="?page={{ projects.previous_page_number }}&kw={{kw}}&sort={{sort}}&role={{role}}" class="layui-btn layui-btn-xs layui-btn-normal">上一页</a>
{% else %}
<a href="javascript:;" class="layui-btn layui-btn-xs layui-btn-disabled">上一页</a>
{% endif %}
@ -94,12 +220,13 @@
</span>
<!-- 下一页 -->
{% if projects.has_next %}
<a href="?page={{ projects.next_page_number }}&kw={{projects.kw}}" class="layui-btn layui-btn-xs layui-btn-normal">下一页</a>
<a href="?page={{ projects.next_page_number }}&kw={{kw}}&sort={{sort}}&role={{role}}" class="layui-btn layui-btn-xs layui-btn-normal">下一页</a>
{% else %}
<a class="layui-btn layui-btn-xs layui-btn-disabled">下一页</a>
{% endif %}
</div>
</div>
<!-- 分页结束 -->
<!-- 页脚 -->
{% include 'app_doc/foot_base.html' %}
@ -113,6 +240,53 @@
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
var layer = layui.layer;
var form = layui.form;
function positionFooter() {
// 获取页脚的高度
footerHeight = $(".layui-footer").height();
// 获取页脚的高度
/*
scrollTop() 设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离
*/
footerTop = ($(window).scrollTop() + $(window).height() - footerHeight - 40)+"px";
// console.log("页脚高度:",footerHeight)
// console.log(footerTop)
// console.log($(".layui-header").height()+$(".layui-container").height())
// console.log("窗口高度:",$(window).height())
//如果页面内容高度小于屏幕高度div#footer将绝对定位到屏幕底部否则div#footer保留它的正常静态定位
if(($(".layui-header").height() + $(".index-control").height() + $(".project-list-content").height() + $(".project-list-page").height() + 15) < $(window).height()) {
console.log("页脚置底")
$(".layui-footer").css({ position: "absolute",left:"0" }).stop().css({top:footerTop});
}else{
$(".layui-footer").css({ position: ""})
}
};
$(window).bind("load", function() {
// 设置页脚位置
var footerHeight = 0;
var footerTop = 0;
positionFooter();
//$(window).scroll(positionFooter).resize(positionFooter);
//设置条件栏选中值
var url = layui.url();
console.log(url)
$("#sel-role").val(url.search.role);
$("#sel-sort").val(url.search.sort);
layui.form.render('select');
});
// 侦听Select下拉框的选择事件
form.on('select()', function(data){
var filter_data = form.val("filter-project-form");
console.log(filter_data)
window.location.href = '/?sort=' + filter_data['sort'] + '&role=' + filter_data['role'] + '&kw={{kw}}'
});
</script>
<script>
//创建新文集
createPro = function () {
layer.open({
@ -124,11 +298,13 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'pname':$("#pname").val(),
'desc':$("#desc").val(),
}
$.post("{% url 'create_project' %}",data,function(r){
layer.closeAll("loading");
if(r.status){
//创建成功,刷新页面
window.location.reload();
@ -152,18 +328,21 @@
btn:['确定','取消'], //添加按钮
btnAlign:'c', //按钮居中
yes:function (index,layero) {
layer.load(1);
data = {
'pro_id':pro_id,
'name':$("#pname").val(),
'desc':$("#desc").val(),
}
$.post("{% url 'modify_project' %}",data,function(r){
layer.closeAll("loading");
if(r.status){
//修改成功,刷新页面
window.location.reload();
}else{
//修改失败,提示
console.log(r)
// console.log(r)
layer.msg(r.data)
}
})
},
@ -179,11 +358,13 @@
content:'<div style="padding:10px 0 0 20px;">修改用户[' + username + ']的密码:</div><div style="padding: 20px;"><input class="layui-input" type="password" id="newPwd1" style="margin-bottom:10px;" placeholder="输入新密码" required lay-verify="required"><input class="layui-input" type="password" id="newPwd2" placeholder="再次确认新密码" required lay-verify="required"></div>',
btn:['确认修改','取消'],
yes:function (index,layero) {
layer.load(1);
data = {
'password':$("#newPwd1").val(),
'password2':$("#newPwd2").val(),
}
$.post("{% url 'modify_pwd' %}",data,function(r){
layer.closeAll("loading");
if(r.status){
//修改成功
window.location.reload();
@ -197,6 +378,18 @@
},
})
};
//切换文集列表和网格
$("#switch-grid-list").click(function(){
// 切换按钮图标
$(this).children("i").toggleClass("layui-icon-align-left layui-icon-app");//切换图标
// 切换class
$(".project-item").toggleClass("layui-hide")
$(".project-item-list").toggleClass("layui-hide")
// 重新计算底部
var footerHeight = 0;
var footerTop = 0;
positionFooter();
});
</script>
<!-- 统计代码开始 -->
{% if debug %}
@ -204,32 +397,6 @@
{{ static_code | safe }}
{% endif %}
<!-- 统计代码结束 -->
<script>
$(window).bind("load", function() {
var footerHeight = 0;
var footerTop = 0;
positionFooter();
function positionFooter() {
// 获取页脚的高度
footerHeight = $(".layui-footer").height();
// 获取页脚的高度
/*
scrollTop() 设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离
*/
footerTop = ($(window).scrollTop()+$(window).height()-footerHeight-40)+"px";
console.log(footerHeight)
console.log(footerTop)
console.log($(".layui-header").height()+$(".layui-container").height())
console.log($(window).height())
//如果页面内容高度小于屏幕高度div#footer将绝对定位到屏幕底部否则div#footer保留它的正常静态定位
if(($(".layui-header").height() + $(".project-list-content").height() + $(".project-list-page").height()) < $(window).height()) {
console.log("页脚置底")
$(".layui-footer").css({ position: "absolute",left:"0" }).stop().css({top:footerTop});
}
}
//$(window).scroll(positionFooter).resize(positionFooter);
});
</script>
{% endblock %}
</body>
</html>

View File

@ -9,18 +9,23 @@
{% if request.user == project.create_user or colla_user > 0 %}
<span class="btn pull-left">|</span>
<a class="btn pull-left" href="{% url 'create_doc' %}?pid={{project.id}}" target="_blank">
<i class="fa fa-plus-square"></i> 添加
<i class="fa fa-plus-square"></i> <span class="layui-hide-xs">添加</span>
</a>
{% endif %}
{% if request.user == project.create_user %}
<a class="btn pull-left" href="{% url 'manage_project' %}" target="_blank">
<i class="fa fa-cubes"></i> 管理
<i class="fa fa-cubes"></i> <span class="layui-hide-xs">管理</span>
</a>
{% endif %}
{% endblock %}
{% block content_head %}
<h1>{{ project.name }}</h1><hr>
<p style="color: #c2c2c2;font-size:12px;">
<!--<i class="fa fa-th-large"></i> 发表:{{ doc.create_time }}-->
<i class="fa fa-user"></i> 创建人:{{ project.create_user.username }}
&nbsp;&nbsp;&nbsp;&nbsp;<i class="fa fa-timer"></i> 创建于:{{ project.create_time }}
</p>
{% endblock %}
{% block page_content %}