forked from mirror/MrDoc
新增本地文档批量导入指定文档功能
This commit is contained in:
parent
5989f06774
commit
3c4e0168af
@ -20,12 +20,15 @@ urlpatterns = [
|
||||
path('manage_project_colla/<int:pro_id>/',views.manage_project_collaborator,name="manage_pro_colla"), # 管理文集协作
|
||||
path('manage_pro_colla_self/',views.manage_pro_colla_self,name="manage_pro_colla_self"), # 我协作的文集
|
||||
path('manage_project_import/',views_import.import_project,name="import_project"), # 导入文集
|
||||
path('import_doc_to_project/', views_import.import_local_doc_to_project, name="import_doc_to_project"), # 导入本地文档到文集
|
||||
path('manage_project_doc_sort/',views_import.project_doc_sort,name='project_doc_sort'), # 导入文集文档排序
|
||||
path('manage_project_transfer/<int:pro_id>/',views.manage_project_transfer,name='manage_pro_transfer'), # 文集转让
|
||||
path('manage_pro_doc_sort/<int:pro_id>/',views.manage_project_doc_sort,name='manage_pro_doc_sort'), # 文集排序
|
||||
path('api/my_colla_list/', views.MyCollaList.as_view(), name="my_colla_list"), # 我的协作文集列表
|
||||
path('api/import_local_doc/', views_import.ImportLocalDoc.as_view(), name="import_local_doc_api"), # 导入本地文档API
|
||||
#################文档相关
|
||||
path('project-<int:pro_id>/doc-<int:doc_id>/', views.doc, name='doc'), # 文档浏览页
|
||||
path('doc/<int:doc_id>/', views.doc_id, name="doc_id"), # 文档浏览页(通过文档ID)
|
||||
path('create_doc/', views.create_doc, name="create_doc"), # 新建文档
|
||||
path('modify_doc/<int:doc_id>/', views.modify_doc, name="modify_doc"), # 修改文档
|
||||
path('del_doc/',views.del_doc,name="del_doc"), # 删除文档
|
||||
|
@ -1071,6 +1071,95 @@ def doc(request,pro_id,doc_id):
|
||||
return render(request,'404.html')
|
||||
|
||||
|
||||
# 文档浏览页,可通过文档ID 或文集ID+文档ID访问
|
||||
@require_http_methods(['GET'])
|
||||
def doc_id(request,doc_id):
|
||||
try:
|
||||
# 获取文档内容
|
||||
try:
|
||||
doc = Doc.objects.get(id=int(doc_id),status__in=[0,1]) # 文档信息
|
||||
doc_tags = DocTag.objects.filter(doc=doc) # 文档标签信息
|
||||
pro_id = doc.top_doc
|
||||
if doc.status == 0 and doc.create_user != request.user:
|
||||
raise ObjectDoesNotExist
|
||||
elif doc.status == 0 and doc.create_user == request.user:
|
||||
doc.name = _('【预览草稿】')+ doc.name
|
||||
|
||||
except ObjectDoesNotExist:
|
||||
return render(request, '404.html')
|
||||
|
||||
# 获取文集信息
|
||||
project = Project.objects.get(id=int(pro_id))
|
||||
# 获取文集的文档目录
|
||||
toc_list,toc_cnt = get_pro_toc(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 request.user.is_authenticated:
|
||||
is_collect_pro = MyCollect.objects.filter(collect_type=2, collect_id=pro_id,
|
||||
create_user=request.user).exists()
|
||||
# 获取文档收藏状态
|
||||
is_collect_doc = MyCollect.objects.filter(collect_type=1, collect_id=doc_id,
|
||||
create_user=request.user).exists()
|
||||
else:
|
||||
is_collect_pro,is_collect_doc = False,False
|
||||
|
||||
# 私密文集且访问者非创建者、协作者 - 不能访问
|
||||
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
|
||||
if request.user.is_authenticated: # 认证用户判断是否在许可用户列表中
|
||||
if (request.user.username not in user_list) and \
|
||||
(request.user != project.create_user) and \
|
||||
(colla_user == 0): # 访问者不在指定用户之中,也不是协作者
|
||||
return render(request, '404.html')
|
||||
else: # 游客直接返回404
|
||||
return render(request, '404.html')
|
||||
# 访问码可见
|
||||
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中的访问码不等于文集访问码,跳转到访问码认证界面
|
||||
return redirect('/check_viewcode/?to={}'.format(request.path))
|
||||
|
||||
# 获取文档内容
|
||||
try:
|
||||
doc = Doc.objects.get(id=int(doc_id),status__in=[0,1]) # 文档信息
|
||||
doc_tags = DocTag.objects.filter(doc=doc) # 文档标签信息
|
||||
if doc.status == 0 and doc.create_user != request.user:
|
||||
raise ObjectDoesNotExist
|
||||
elif doc.status == 0 and doc.create_user == request.user:
|
||||
doc.name = _('【预览草稿】')+ doc.name
|
||||
|
||||
except ObjectDoesNotExist:
|
||||
return render(request, '404.html')
|
||||
# 获取文档分享信息
|
||||
try:
|
||||
doc_share = DocShare.objects.get(doc=doc)
|
||||
is_share = True
|
||||
except ObjectDoesNotExist:
|
||||
is_share = False
|
||||
return render(request,'app_doc/doc.html',locals())
|
||||
except Exception as e:
|
||||
logger.exception(_("文集浏览出错"))
|
||||
return render(request,'404.html')
|
||||
|
||||
|
||||
# 创建文档
|
||||
@login_required()
|
||||
@require_http_methods(['GET',"POST"])
|
||||
|
@ -18,10 +18,17 @@ from django.contrib.auth.models import User
|
||||
from django.db.models import Q
|
||||
from django.db import transaction
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework.views import APIView # 视图
|
||||
from rest_framework.response import Response # 响应
|
||||
from rest_framework.pagination import PageNumberPagination # 分页
|
||||
from rest_framework.authentication import SessionAuthentication # 认证
|
||||
from rest_framework.permissions import IsAdminUser # 权限
|
||||
from loguru import logger
|
||||
from app_doc.report_utils import *
|
||||
from app_admin.decorators import check_headers,allow_report_file
|
||||
from app_doc.import_utils import *
|
||||
from app_doc.views import get_pro_toc,html_filter,jsonXssFilter
|
||||
from app_api.auth_app import AppAuth,AppMustAuth # 自定义认证
|
||||
import datetime
|
||||
import traceback
|
||||
import re
|
||||
@ -133,6 +140,144 @@ def import_project(request):
|
||||
return JsonResponse({'status':False,'data':_('参数错误')})
|
||||
|
||||
|
||||
# 导入本地文档到文集
|
||||
@login_required()
|
||||
@require_http_methods(['GET','POST'])
|
||||
def import_local_doc_to_project(request):
|
||||
if request.method == 'GET':
|
||||
project_list = Project.objects.filter(create_user=request.user) # 自己创建的文集列表
|
||||
colla_project_list = ProjectCollaborator.objects.filter(user=request.user) # 协作的文集列表
|
||||
return render(request,'app_doc/manage/import_local_doc_to_project.html',locals())
|
||||
|
||||
|
||||
# 导入文档到文集API
|
||||
class ImportLocalDoc(APIView):
|
||||
authentication_classes = [SessionAuthentication, AppMustAuth]
|
||||
|
||||
# 上传文件
|
||||
def post(self,request):
|
||||
project = request.data.get("project",'')
|
||||
editor_mode = request.data.get("editor_mode",0)
|
||||
file = request.data.get("local_doc",None)
|
||||
try:
|
||||
project = int(project)
|
||||
editor_mode = int(editor_mode)
|
||||
except:
|
||||
resp = {
|
||||
'code':5,
|
||||
'data':'必须选择文集'
|
||||
}
|
||||
return Response(resp)
|
||||
if file is None:
|
||||
resp = {
|
||||
'code':5,
|
||||
'data':'文件未选择'
|
||||
}
|
||||
file_name = file.name
|
||||
# Markdown 文件和 TXT 文件
|
||||
if file_name.endswith('.md') or file_name.endswith(".txt"):
|
||||
doc_content = file.read().decode('utf-8')
|
||||
if editor_mode == 3:
|
||||
doc_content_html = markdown.markdown(text=doc_content)
|
||||
else:
|
||||
doc_content_html = None
|
||||
doc = Doc.objects.create(
|
||||
name = html_filter(file_name[:-3]),
|
||||
pre_content = doc_content,
|
||||
content = doc_content_html,
|
||||
top_doc = project,
|
||||
editor_mode = 1 if editor_mode == 0 else editor_mode,
|
||||
create_user = request.user,
|
||||
status = 0
|
||||
)
|
||||
doc.save()
|
||||
resp = {
|
||||
'code':0,
|
||||
'data':{
|
||||
'doc_id':doc.id,
|
||||
'doc_name':doc.name
|
||||
}
|
||||
}
|
||||
# Word 文件
|
||||
elif file_name.endswith('.docx'):
|
||||
if os.path.exists(os.path.join(settings.MEDIA_ROOT, 'import_temp')) is False:
|
||||
os.mkdir(os.path.join(settings.MEDIA_ROOT, 'import_temp'))
|
||||
|
||||
temp_file_name = str(time.time()) + '.docx'
|
||||
temp_file_path = os.path.join(settings.MEDIA_ROOT, 'import_temp/' + temp_file_name)
|
||||
with open(temp_file_path, 'wb+') as docx_file:
|
||||
for chunk in file:
|
||||
docx_file.write(chunk)
|
||||
if os.path.exists(temp_file_path):
|
||||
docx_file_content = ImportDocxDoc(
|
||||
docx_file_path=temp_file_path,
|
||||
editor_mode=editor_mode,
|
||||
create_user=request.user
|
||||
).run()
|
||||
if docx_file_content['status']:
|
||||
doc = Doc.objects.create(
|
||||
name=html_filter(file_name[:-5]),
|
||||
pre_content=docx_file_content['data'],
|
||||
content=docx_file_content['data'],
|
||||
top_doc=project,
|
||||
editor_mode=1 if editor_mode == 0 else editor_mode,
|
||||
create_user=request.user,
|
||||
status=0
|
||||
)
|
||||
doc.save()
|
||||
resp = {
|
||||
'code': 0,
|
||||
'data': {
|
||||
'doc_id': doc.id,
|
||||
'doc_name': doc.name
|
||||
}
|
||||
}
|
||||
else:
|
||||
resp = {
|
||||
'code':4,
|
||||
'data': '{}读取失败'.format(file_name)
|
||||
}
|
||||
else:
|
||||
resp = {
|
||||
'code': 4,
|
||||
'data': '{}上传失败'.format(file_name)
|
||||
}
|
||||
else:
|
||||
resp = {
|
||||
'code':5,
|
||||
'data':'文件格式不支持'
|
||||
}
|
||||
return Response(resp)
|
||||
|
||||
# 发布文档
|
||||
def put(self,request):
|
||||
sort_data = request.data.get('sort_data', '[]') # 文档排序列表
|
||||
try:
|
||||
sort_data = json.loads(sort_data)
|
||||
except Exception:
|
||||
return JsonResponse({'code': 5, 'data': _('文档参数错误')})
|
||||
# 文档排序
|
||||
n = 10
|
||||
# 第一级文档
|
||||
for data in sort_data:
|
||||
Doc.objects.filter(id=data['id']).update(sort=n, status=1)
|
||||
n += 10
|
||||
# 存在第二级文档
|
||||
if 'children' in data.keys():
|
||||
n1 = 10
|
||||
for c1 in data['children']:
|
||||
Doc.objects.filter(id=c1['id']).update(sort=n1, parent_doc=data['id'], status=1)
|
||||
n1 += 10
|
||||
# 存在第三级文档
|
||||
if 'children' in c1.keys():
|
||||
n2 = 10
|
||||
for c2 in c1['children']:
|
||||
Doc.objects.filter(id=c2['id']).update(sort=n2, parent_doc=c1['id'], status=1)
|
||||
|
||||
return Response({'code':0,'data':'ok'})
|
||||
|
||||
|
||||
|
||||
# 文集文档排序
|
||||
@login_required()
|
||||
@require_http_methods(['POST'])
|
||||
|
1
static/icon_img/file-doc.svg
Normal file
1
static/icon_img/file-doc.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632207650207" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3385" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M1004.284179 311.581294v491.723781c0 50.435821-37.801393 88.288159-81.970945 88.288159h-81.970945v-44.118607h81.970945a43.099701 43.099701 0 0 0 44.118607-44.118607V343.116418H802.38806c-44.118607 0-81.970945-37.801393-81.970946-88.237214V59.402189h-371.900497a43.099701 43.099701 0 0 0-44.118607 44.118607h-37.750448C266.647562 53.084975 298.029851 15.283582 348.618507 15.283582h397.373135l252.179104 290.031443m-239.442786-208.060498v163.890946a43.099701 43.099701 0 0 0 43.6601 44.118607h138.72398z m37.801393 346.733533v491.774726c0 50.435821-37.801393 88.237214-81.970945 88.237214H140.55801C96.388458 1024 58.587065 986.147662 58.587065 935.711841V235.927562C58.587065 185.491741 96.388458 147.741294 140.55801 147.741294h397.373134l252.179105 289.980497m-239.442786-208.060497v163.890945a43.099701 43.099701 0 0 0 44.118607 44.118607h138.72398z m-447.961791 6.266268v699.784279a43.099701 43.099701 0 0 0 44.118607 44.118607h573.745671a43.099701 43.099701 0 0 0 44.118607-44.118607V475.523184h-170.208159c-44.118607 0-81.970945-37.852338-81.970945-88.288159V191.808955h-371.900498c-25.472637 0-37.801393 18.900697-37.801393 44.118607z m100.871641 264.915423h201.743284v44.118607h-201.743284z m0-132.457711h201.743284V412.656716h-201.743284z m453.922388 302.614925h-453.922388V626.626866h453.922388z m0 132.457711h-453.922388V759.084577h453.922388z m0 0" p-id="3386"></path></svg>
|
After Width: | Height: | Size: 1.7 KiB |
@ -57,6 +57,17 @@
|
||||
color:red;
|
||||
}
|
||||
|
||||
/* 表格列中的链接样式 */
|
||||
.table-col-link{
|
||||
color:#2D8CF0;
|
||||
font-weight: 700;
|
||||
}
|
||||
.table-col-link:hover{
|
||||
color:#2D8CF0;
|
||||
text-decoration: underline;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 覆盖layUI样式 */
|
||||
.layui-layout-admin .layui-body{
|
||||
/* background-color: #f0f2f5; */
|
||||
|
327
template/app_doc/manage/import_local_doc_to_project.html
Normal file
327
template/app_doc/manage/import_local_doc_to_project.html
Normal file
@ -0,0 +1,327 @@
|
||||
{% extends 'app_doc/user/user_base.html' %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "导入文集" %}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-row" style="padding-left:15px;">
|
||||
<span class="layui-breadcrumb" lay-separator=">">
|
||||
<a href="{% url 'import_project' %}">{% trans "导入文集" %}</a>
|
||||
<a><cite>{% trans "导入本地文档到文集" %}</cite></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导入本地文档到文集 -->
|
||||
<div class="layui-row layui-col-space10">
|
||||
<!-- 文集设置 -->
|
||||
<div class="layui-col-md6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-card-header" style="margin-bottom: 10px;">
|
||||
<span style="font-size:18px;">{% trans "文档文集设置" %}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="layui-form" lay-filter="project-settings">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">选择文集</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="project" lay-verify="required">
|
||||
<option value="">请选择一个文集(必选)</option>
|
||||
<!-- 自己的文集 -->
|
||||
<optgroup label="自有文集" id="self-project">
|
||||
{% for p in project_list %}
|
||||
{% if p.role == 1 %}
|
||||
<option value="{{ p.id }}">[私密]《{{ p.name }}》</option>
|
||||
{% elif p.role == 2 %}
|
||||
<option value="{{ p.id }}" >[指定用户]《{{ p.name }}》</option>
|
||||
{% elif p.role == 3 %}
|
||||
<option value="{{ p.id }}" >[访问码]《{{ p.name }}》</option>
|
||||
{% else %}
|
||||
<option value="{{ p.id }}" >[公开]《{{ p.name }}》</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</optgroup>
|
||||
<!-- 协作的文集 -->
|
||||
{% if colla_project_list.count > 0 %}
|
||||
<optgroup label="协作文集">
|
||||
{% for p in colla_project_list %}
|
||||
<option value="{{ p.project.id }}">[协作]《{{ p.project.name }}》</option>
|
||||
{% endfor %}
|
||||
</optgroup>
|
||||
{% endif %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux"><span style="color:red;">*必选</span></div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">文档模式</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="editor_mode" value="0" title="自动选择" checked>
|
||||
<input type="radio" name="editor_mode" value="1" title="Markdown">
|
||||
<input type="radio" name="editor_mode" value="3" title="富文本">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 文件上传 -->
|
||||
<div class="layui-col-md6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-card-header" style="margin-bottom: 10px;">
|
||||
<span style="font-size:18px;">{% trans "文档上传" %}</span>
|
||||
</div>
|
||||
<div>
|
||||
<button style="width: 100%;min-height: 113px;border-style:dashed;border-color: #999;cursor: pointer;" type="button" id="import-local-doc">
|
||||
<i class="layui-icon layui-icon-addition" style="font-size: 40px;color:#999"></i><br>
|
||||
点击上传本地文档,支持.md、.docx、.txt格式文件
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 文档排序 -->
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md12">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-card-header" style="margin-bottom: 10px;">
|
||||
<span style="font-size:18px;">{% trans "文档排序(文档导入后状态为草稿)" %}</span>
|
||||
<button class="pear-btn pear-btn-primary pear-btn-sm" style="float: right;" id="save-sort-btn">{% trans "发布文档" %}</button>
|
||||
</div>
|
||||
<div>
|
||||
<div id="nested" class="row">
|
||||
<ul id="nestedDemo" class="list-group col nested-sortable"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block custom_script %}
|
||||
<script src="{% static 'jquery/3.1.1/jquery.min.js' %}"></script>
|
||||
<script src="{% static 'layui/layui.js' %}"></script>
|
||||
<script>
|
||||
$.ajaxSetup({
|
||||
headers: {"X-CSRFToken":'{{ csrf_token }}'},
|
||||
});
|
||||
var upload = layui.upload,form=layui.form;
|
||||
// 上传文件按钮点击事件
|
||||
var uploadInst = upload.render({
|
||||
elem: '#import-local-doc', //绑定元素
|
||||
url: "{% url 'import_local_doc_api' %}", //上传接口
|
||||
field:"local_doc",
|
||||
multiple:true,
|
||||
accept:'file',
|
||||
exts:'md|txt|docx',
|
||||
before: function(obj){ //obj参数包含的信息,跟 choose回调完全一致,可参见上文。
|
||||
this.data = form.val("project-settings");
|
||||
layer.load(); //上传loading
|
||||
},
|
||||
done: function(res){
|
||||
layer.closeAll();
|
||||
//上传完毕回调
|
||||
if(res.code == 0){
|
||||
let doc_toc_str = '<li data-sortable-id="'
|
||||
+ res.data['doc_id']
|
||||
+ '" class="list-group-item"><i class="iconfont mrdoc-icon-wendang"></i> '
|
||||
+ res.data['doc_name']
|
||||
+ ' <a class="table-col-link" target="_blank" href="/doc/' + res.data['doc_id'] + '/">预览</a>'
|
||||
+ '<ul class="list-group nested-sortable"></ul></li>'
|
||||
$("#nestedDemo").append(doc_toc_str);
|
||||
docSort();// 动态添加元素之后,调用一次
|
||||
}else{
|
||||
layer.msg(res.data,{'icon':2})
|
||||
}
|
||||
},
|
||||
error: function(){
|
||||
layer.closeAll();
|
||||
//请求异常回调
|
||||
layer.msg("上传失败",{icon:2});
|
||||
}
|
||||
});
|
||||
// 发布文档
|
||||
$("#save-sort-btn").on('click',function(){
|
||||
layer.load(1)
|
||||
let sort_data = {
|
||||
'sort_data':JSON.stringify(serialize(root)),
|
||||
}
|
||||
// console.log(sort_data)
|
||||
$.ajax({
|
||||
url:"{% url 'import_local_doc_api' %}",
|
||||
type:'put',
|
||||
data:sort_data,
|
||||
success:function(r){
|
||||
layer.closeAll();
|
||||
if(r.code == 0){
|
||||
layer.msg("发布成功",{icon:1},function(){
|
||||
window.location.reload();
|
||||
})
|
||||
}else{
|
||||
layer.msg(r.data,{icon:2})
|
||||
}
|
||||
},
|
||||
error:function(){
|
||||
layer.closeAll();
|
||||
layer.msg("发布失败",{icon:2})
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
// 删除文档
|
||||
|
||||
</script>
|
||||
<!-- 导入的文集文档排序模板div -->
|
||||
<style>
|
||||
.row {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.col {
|
||||
-ms-flex-preferred-size: 0;
|
||||
flex-basis: 0;
|
||||
-ms-flex-positive: 1;
|
||||
flex-grow: 1;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.list-group-item:first-child {
|
||||
border-top-left-radius: .25rem;
|
||||
border-top-right-radius: .25rem;
|
||||
}
|
||||
.list-group-item {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: .75rem 1.25rem;
|
||||
margin-bottom: -1px;
|
||||
background-color: #fff;
|
||||
border: 1px solid rgba(0,0,0,.125);
|
||||
}
|
||||
.list-group-item:first-child {
|
||||
border-top-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.list-group-item:last-child {
|
||||
margin-bottom: 0;
|
||||
border-bottom-right-radius: 0.25rem;
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.list-group-item:hover, .list-group-item:focus {
|
||||
z-index: 1;
|
||||
text-decoration: none;
|
||||
}
|
||||
.list-group-item.disabled, .list-group-item:disabled {
|
||||
color: #6c757d;
|
||||
pointer-events: none;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.list-group-item.active {
|
||||
z-index: 2;
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="import-project-sort" style="display: none;margin: 10px;" class="layui-form">
|
||||
<div class="layui-row" style="padding-left: 14px;padding-bottom: 10px;">
|
||||
<input class="layui-input" placeholder="请输入文集名称" name="project-name">
|
||||
</div>
|
||||
<div class="layui-row" style="padding-left: 14px;padding-bottom: 10px;">
|
||||
<input class="layui-input" placeholder="请输入文集简介" name="project-desc">
|
||||
</div>
|
||||
<div class="layui-row" style="">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" style="width: auto;">文集状态</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="role" value="1" title="私密" checked>
|
||||
<input type="radio" name="role" value="0" title="公开">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-row" style="padding-left: 14px;padding-bottom: 10px;">文档拖拽排序</div>
|
||||
<div id="nested" class="row">
|
||||
<ul id="nestedDemo" class="list-group col nested-sortable"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 文档拖拽排序 -->
|
||||
<script src="{% static 'sortablejs/Sortable.js' %}"></script>
|
||||
<script>
|
||||
// 文档动态排序
|
||||
function docSort(){
|
||||
// Nested demo
|
||||
var nestedSortables = [].slice.call(document.querySelectorAll('.nested-sortable'));
|
||||
// Loop through each nested sortable element
|
||||
for (var i = 0; i < nestedSortables.length; i++) {
|
||||
new Sortable(nestedSortables[i], {
|
||||
group: {
|
||||
name:'docsort',
|
||||
pull: function(event) {
|
||||
var deep = event.el.parentNode.parentNode.parentNode.parentNode.className;
|
||||
// if(deep == 'list-group nested-sortable') return false;
|
||||
return true;
|
||||
},
|
||||
},
|
||||
animation: 150,
|
||||
fallbackOnBody: true,
|
||||
invertSwap:true,
|
||||
swapThreshold: 0.65,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const nestedQuery = '.nested-sortable';
|
||||
const identifier = 'sortableId';
|
||||
const root = document.getElementById('nestedDemo');
|
||||
function serialize(sortable) {
|
||||
var serialized = [];
|
||||
var children = [].slice.call(sortable.children);
|
||||
for (var i in children) {
|
||||
var nested = children[i].querySelector(nestedQuery);
|
||||
serialized.push({
|
||||
id: children[i].dataset[identifier],
|
||||
children: nested ? serialize(nested) : []
|
||||
});
|
||||
}
|
||||
return serialized
|
||||
}
|
||||
function getLevel(){
|
||||
console.log(serialize(root))
|
||||
}
|
||||
// 展开收起左边目录
|
||||
$(function(){
|
||||
$("body").on('click','.switch-toc',SwitchToc)
|
||||
});
|
||||
function SwitchToc(i){
|
||||
console.log("点击了")
|
||||
var $me = $(this);
|
||||
$(this).next("ul").toggleClass("toc-close"); //切换展开收起样式
|
||||
$(this).toggleClass("layui-icon-left layui-icon-down");//切换图标
|
||||
};
|
||||
form.render();
|
||||
</script>
|
||||
{% endblock %}
|
@ -40,6 +40,26 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导入本地文档到文集 -->
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-card-header" style="margin-bottom: 10px;">
|
||||
<span style="font-size:18px;">{% trans "导入本地文档到文集" %}</span>
|
||||
</div>
|
||||
<div>
|
||||
<a style="width: 142px;cursor: pointer;display: inline-block;" href="{% url 'import_doc_to_project' %}">
|
||||
<div style="width: 70px;height: 70px;margin: 0 auto;">
|
||||
<img src="{% static 'icon_img/file-doc.svg' %}">
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
<div style="color: #262626;font-size:14px;">本地文本文档</div>
|
||||
<div style="color: #8c8c8c;font-size:12px;">支持Markdown、TXT、Word等格式文件</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block custom_script %}
|
||||
<script src="{% static 'jquery/3.1.1/jquery.min.js' %}"></script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user