### ztree文档
http://www.treejs.cn/v3/api.php
### django配置
#### 创建虚拟环境
```bash
virtualenv venv27
source venv27/bin/activate
```
#### 安装django
```bash
pip install django binaryornot
```
#### 创建项目
```bash
django-admin startproject myproject
```
#### 创建app
```bash
cd myproject
python manage.py startapp blog
```
#### 配置项目settings.py
```bash
vim myproject/settings.py
```
```ini
# -*- coding:utf-8 -*-
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
...
'blog',#安装app
]
MEDIA_URL = "/upload/" #配置上传目录
MEDIA_ROOT = os.path.join(BASE_DIR, 'upload')
STATIC_URL = '/static/' #配置静态文件目录
STATIC_DIR = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = [STATIC_DIR, ]
```
#### 创建上传和静态文件目录
```bash
mkdir myproject/{upload,static}
```
#### 配置项目urls.py
```bash
vim myproject/urls.py
```
```python
# -*- coding:utf-8 -*-
from django.conf.urls import url,include
from django.conf.urls.static import static
from . import settings
urlpatterns = [
url(r'^blog/', include('blog.urls', namespace='blog')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```
#### 配置应用urls.py
```bash
vim blog/urls.py
```
```python
# -*- coding:utf-8 -*-
from django.conf.urls import url
from blog import views,filetree_views
urlpatterns = [
url(r'^demo/$', views.demo, name='demo'),#展示视图
url(r'^async_get_nodes/', filetree_views.async_get_nodes, name='async_get_nodes'), #异步加载节点
url(r'^rename_node/', filetree_views.rename_node, name='rename_node'), #重命名节点
url(r'^add_node/', filetree_views.add_node, name='add_node'), #添加节点
url(r'^remove_node/', filetree_views.remove_node, name='remove_node'), #移除节点
url(r'^move_node/', filetree_views.move_node, name='move_node'), #移动节点
url(r'^read_node/', filetree_views.read_node, name='read_node'), #读取节点
url(r'^save_node/', filetree_views.save_node, name='save_node'), #保存节点
url(r'^upload_file/', filetree_views.upload_file, name='upload_file'), #节点上传文件
url(r'^upload_folder/', filetree_views.upload_folder, name='upload_folder'), #节点上传文件夹
url(r'^download_node/', filetree_views.download_node, name='download_node'), #下载节点
]
```
#### 配置项目views.py
```bash
vim blog/views.py
```
```python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
from django.conf import settings
from datetime import datetime
import random
import base64
import os
code_root_path=os.path.join(settings.BASE_DIR,'upload/pygame/code_folder_path')
def demo(request):
#获取加密的参数
code_folder_enc=request.get_signed_cookie("code_folder_enc",default='',salt='ynotes')
if not code_folder_enc:
#创建随机目录
datetime_str=datetime.now().strftime('%Y%m%d%H%M%S%f')
random_str=random.random()
code_folder="{0}__{1}".format(datetime_str,random_str)
code_folder_enc=base64.b64encode(code_folder)
code_folder_path=code_folder
code_folder_abspath=os.path.join(code_root_path,code_folder_path)
os.makedirs(code_folder_abspath)
else:
#解密参数值
code_folder=base64.b64decode(code_folder_enc)
code_folder_path=code_folder
response = render(request, 'blog/demo.html',{'code_folder_path': code_folder_path})
response.set_signed_cookie('code_folder_enc',code_folder_enc,salt='ynotes',max_age=864000)
return response
```
#### 配置项目filetree_views.py
```bash
vim blog/filetree_views.py
```
```python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.http import HttpResponse
from django.shortcuts import render
from django.conf import settings
import os
import json
import shutil
import base64
import zipfile
import random
from datetime import datetime
from binaryornot.check import is_binary
code_relative_path='upload/pygame/code_folder_path'
code_root_path=os.path.join(settings.BASE_DIR,code_relative_path)
#zip压缩目录
def zipdir(path, ziph):
for root, dirs, files in os.walk(path):
for file in files:
ziph.write(os.path.join(root, file))
def async_get_nodes(request):
"""异步加载节点"""
code_folder_path=request.POST.get('codeFolderPath','')#获取代码的临时目录
code_folder_abspath=os.path.join(code_root_path,code_folder_path)#拼接临时目录绝对路径
treenode_path=request.POST.get('treeNodePath','') #获取选中节点的相对路径
get_nodes_dir=os.path.join(code_folder_abspath,treenode_path) #拼接选中节点的绝对路径
nodes_list=[]
#如果code_folder_path参数为空或者绝对路径不存在返回空列表json
if not code_folder_path or not os.path.exists(code_folder_abspath):
return HttpResponse(json.dumps([]))
#循环拼接ztree的数据
for i in os.listdir(get_nodes_dir):
node_dict={}
get_file=os.path.join(get_nodes_dir,i)
inode=os.stat(get_file).st_ino
node_dict['id']=inode
node_dict['name']=i
#通过文件后缀设置不同的zTree图标
if os.path.isdir(get_file):
node_dict['isParent']='true'
else:
node_dict['isParent']='false'
nodes_list.append(node_dict)
return HttpResponse(json.dumps(nodes_list,ensure_ascii=False))
def rename_node(request):
"""重命名节点"""
code_folder_path=request.POST.get('codeFolderPath','')
code_folder_abspath=os.path.join(code_root_path,code_folder_path)
treenode_path=request.POST.get('treeNodePath','')
old_path_name=os.path.join(code_folder_abspath,treenode_path)
new_name=request.POST.get('newName','')
if not code_folder_abspath:
return HttpResponse('{"result":"error","msg":"参数异常"}')
if not new_name:
return HttpResponse('{"result":"error","msg":"请输入文件名"}')
new_path_name=os.path.join(os.path.dirname(os.path.join(code_folder_abspath,treenode_path)),new_name)
try:
if os.path.exists(new_path_name):
return HttpResponse('{"result":"error","msg":"文件名已存在"}')
os.rename(old_path_name,new_path_name)
except:
return HttpResponse('{"result":"error","msg":"重命名异常"}')
return HttpResponse('{"result":"ok","msg":"重命名完成"}')
def add_node(request):
"""添加节点"""
code_folder_path=request.POST.get('codeFolderPath','')
code_folder_abspath=os.path.join(code_root_path,code_folder_path)
treenode_path=request.POST.get('treeNodePath','')
file_type=request.POST.get('fileType','file')
new_name=request.POST.get('newName','')
if not code_folder_abspath:
return HttpResponse('{"result":"error","msg":"参数异常"}')
if not new_name:
return HttpResponse('{"result":"error","msg":"请输入文件名"}')
new_path_name=os.path.join(code_folder_abspath,treenode_path,new_name)
try:
if os.path.exists(new_path_name):
return HttpResponse('{"result":"error","msg":"文件名已存在"}')
if file_type == 'file':
os.mknod(new_path_name,0644)
else:
os.mkdir(new_path_name)
except Exception as e:
return HttpResponse('{"result":"error","msg":"新建节点异常"}')
return HttpResponse('{"result":"ok","msg":"新建节点完成"}')
def remove_node(request):
"""移除节点"""
code_folder_path=request.POST.get('codeFolderPath','')
code_folder_abspath=os.path.join(code_root_path,code_folder_path)
treenode_path=request.POST.get('treeNodePath','')
name=request.POST.get('name','')
if not code_folder_abspath:
return HttpResponse('{"result":"error","msg":"参数异常"}')
if not name:
return HttpResponse('{"result":"error","msg":"节点名为空"}')
path_name=os.path.join(code_folder_abspath,treenode_path,name)
try:
if not os.path.exists(path_name):
return HttpResponse('{"result":"error","msg":"节点不存在"}')
else:
if os.path.isdir(path_name):
shutil.rmtree(path_name)
else:
os.remove(path_name)
except Exception as e:
return HttpResponse('{"result":"error","msg":"删除节点异常"}')
return HttpResponse('{"result":"ok","msg":"删除成功"}')
def move_node(request):
"""移动节点"""
code_folder_path=request.POST.get('codeFolderPath','')
code_folder_abspath=os.path.join(code_root_path,code_folder_path)
src_treenode_path=request.POST.get('srcTreeNodePath','')
dst_treenode_path=request.POST.get('dstTreeNodePath','')
src_path_name=os.path.join(code_folder_abspath,src_treenode_path)
dst_path_name=os.path.join(code_folder_abspath,dst_treenode_path)
if not code_folder_abspath:
return HttpResponse('{"result":"error","msg":"参数异常"}')
try:
if not os.path.exists(src_path_name):
return HttpResponse('{"result":"error","msg":"原节点不存在"}')
if not os.path.exists(dst_path_name):
return HttpResponse('{"result":"error","msg":"目的节点不存在"}')
shutil.move(src_path_name,dst_path_name)
except Exception as e:
return HttpResponse('{"result":"error","msg":"移动节点异常"}')
return HttpResponse('{"result":"ok","msg":"移动成功"}')
def read_node(request):
"""读取节点"""
code_folder_path=request.POST.get('codeFolderPath','')
code_folder_abspath=os.path.join(code_root_path,code_folder_path)
treenode_path=request.POST.get('treeNodePath','')
path_name=os.path.join(code_folder_abspath,treenode_path)
if not code_folder_abspath:
return HttpResponse('{"result":"error","msg":"参数异常"}')
if not os.path.exists(path_name):
return HttpResponse('{"result":"error","msg":"文件不存在"}')
print(path_name+"是否是二进制文件",is_binary(path_name))
if not is_binary(path_name):
with open(path_name) as file_object:
content=file_object.read()
data={"result":"ok","msg":"读取完成","data":base64.b64encode(content)}
else:
data={"result":"error","msg":"非文本文件不能读取"}
return HttpResponse(json.dumps(data))
def save_node(request):
"""保存节点,对非二进制文件节点进行保存"""
code_folder_path=request.POST.get('codeFolderPath','')
code_folder_abspath=os.path.join(code_root_path,code_folder_path)
treenode_path=request.POST.get('treeNodePath','')
encode_file_content=request.POST.get('encodedFileContent','') #获取base64编码的文本内容
path_name=os.path.join(code_folder_abspath,treenode_path)
if not code_folder_abspath:
return HttpResponse('{"result":"error","msg":"参数异常"}')
if not os.path.exists(path_name):
return HttpResponse('{"result":"error","msg":"文件不存在"}')
if not is_binary(path_name):
with open(path_name,'w') as file_object:
if len(encode_file_content) > 0:
file_object.write(base64.b64decode(encode_file_content))
else:
file_object.write('')
data={"result":"ok","msg":"保存完成"}
else:
data={"result":"error","msg":"非文本文件不能保存"}
return HttpResponse(json.dumps(data))
def upload_file(request):
"""节点上传文件"""
if request.method == 'POST':
code_folder_path=request.POST.get('codeFolderPath','')
code_folder_abspath=os.path.join(code_root_path,code_folder_path)
treenode_path=request.POST.get('treeNodePath','')
upload_file=request.FILES.get('uploadFile','') #上传的文件
if code_folder_path and upload_file:
upload_file_abspath=os.path.join(code_folder_abspath,treenode_path,upload_file.name) #拼接上传文件后的绝对路径
if os.path.exists(upload_file_abspath) and os.path.isdir(upload_file_abspath):
dict_data={"result":"error","msg":"上传失败,存在同名的目录"}
else:
with open(upload_file_abspath,'w') as f:
f.write(upload_file.read())
if os.path.exists(upload_file_abspath):
inode=os.stat(upload_file_abspath).st_ino
dict_data={"result":"ok","msg":"上传成功","id":inode}
else:
dict_data={"result":"error","msg":"上传文件异常"}
return HttpResponse(json.dumps(dict_data))
return HttpResponse(json.dumps({"result":"error","msg":"请求参数错误"}))
return HttpResponse(json.dumps({"result":"error","msg":"请求方法不支持"}))
def upload_folder(request):
"""节点上传文件夹"""
if request.method == 'POST':
code_folder_path=request.POST.get('codeFolderPath','')
code_folder_abspath=os.path.join(code_root_path,code_folder_path)
treenode_path=request.POST.get('treeNodePath','')
upload_files=request.FILES.getlist('uploadFiles',[]) #获取上传文件列表
upload_file_paths=request.POST.getlist('uploadFilePaths',[]) #获取上传文件的相对路径列表
is_exist_upload_success=False
dict_data={}
#判断上传文件列表和上传文件的相对路径列表是否相等
if code_folder_path and upload_files and len(upload_files) == len(upload_file_paths):
upload_files_dirpath=os.path.join(code_folder_abspath,treenode_path)
upload_fail_files_dict={}
for idx in range(0,len(upload_files)):
#逐级判断是否存在同名的文件
dir_temp_path=''
is_exist_same_file=False
for d in os.path.dirname(upload_file_paths[idx]).split('/'):
dir_temp_path=os.path.join(dir_temp_path,d)
dir_temp_abspath=os.path.join(upload_files_dirpath,dir_temp_path)
if os.path.exists(dir_temp_abspath) and not os.path.isdir(dir_temp_abspath):
is_exist_same_file=True
#需要创建的目录存在同名的文件的字典key是否存在
if dir_temp_path in upload_fail_files_dict:
upload_fail_files_dict[dir_temp_path].append(upload_files[idx].name)
else:
upload_fail_files_dict[dir_temp_path]=[]
upload_fail_files_dict[dir_temp_path].append(upload_files[idx].name)
#如果存在同名的文件没必要往下判断
break
#如果存在同名的文件则直接跳到下一个文件
if is_exist_same_file:
continue
#判断是否存在目录,不存在则创建
upload_file_dir_abspath=os.path.join(upload_files_dirpath,os.path.dirname(upload_file_paths[idx]))
if not os.path.exists(upload_file_dir_abspath):
os.makedirs(upload_file_dir_abspath)
#把文件保存到相应的路径下
#判断上传的文件名是否存在同名的目录
upload_file_abspath=os.path.join(upload_file_dir_abspath,upload_files[idx].name)
if os.path.exists(upload_file_abspath) and os.path.isdir(upload_file_abspath):
if upload_file_abspath in upload_fail_files_dict:
upload_fail_files_dict[upload_file_paths[idx]].append(upload_file_paths[idx])
else:
upload_fail_files_dict[upload_file_paths[idx]]=[]
upload_fail_files_dict[upload_file_paths[idx]].append(upload_file_paths[idx])
continue
with open(upload_file_abspath,'w') as f:
f.write(upload_files[idx].read())
is_exist_upload_success=True
fail_msg=""
if upload_fail_files_dict:
for d in upload_fail_files_dict.keys():
for f in upload_fail_files_dict[d]:
if f == d:
fail_msg+="已存在同名的目录{0},导致文件{1}上传失败.\n".format(d,f)
else:
fail_msg+="已存在同名的文件{0},创建目录{0}失败,导致该目录下的文件{1}上传失败.\n".format(d,f)
if fail_msg:
if is_exist_upload_success:
dict_data["msg"]="警告:\n"+fail_msg
dict_data["result"]="error"
else:
dict_data["msg"]="警告:\n"+fail_msg
dict_data["result"]="error"
else:
dict_data["msg"]="上传文件夹成功"
dict_data["result"]="ok"
##获取父目录的id
if is_exist_upload_success:
inode=os.stat(os.path.join(upload_files_dirpath,upload_file_paths[0].split('/')[0])).st_ino
dict_data["id"]=inode
return HttpResponse(json.dumps(dict_data))
return HttpResponse(json.dumps({"result":"error","msg":"请求参数错误"}))
return HttpResponse(json.dumps({"result":"error","msg":"请求方法不支持"}))
def download_node(request):
"""下载节点"""
if request.method == 'POST':
code_folder_path=request.POST.get('codeFolderPath','')
code_folder_abspath=os.path.join(code_root_path,code_folder_path)
treenode_path=request.POST.get('treeNodePath','')
if code_folder_path:
download_file=os.path.join(code_folder_abspath,treenode_path)
dict_data={}
if os.path.exists(download_file):
if os.path.isdir(download_file):
#生成一个压缩包目录
datetime_str=datetime.now().strftime('%Y%m%d%H%M%S%f')
random_str=random.random()
zip_folder="{0}__{1}".format(datetime_str,random_str)
zip_folder_abspath=os.path.join(code_root_path,'zip',zip_folder)
os.makedirs(zip_folder_abspath)
#生成压缩包
cwd=os.getcwd()
try:
zip_filename = "%s" % os.path.basename(download_file) if treenode_path else "All"
workspace_dir=os.path.dirname(download_file) if treenode_path else code_root_path
os.chdir(workspace_dir)
zip_dirname=os.path.basename(download_file) if treenode_path else code_folder_path
shutil.make_archive(os.path.join(zip_folder_abspath,zip_filename), 'zip',zip_dirname)
os.chdir(cwd)
except Exception as e:
os.chdir(cwd)
#返回zip的下载链接
url=os.path.join(code_relative_path,'zip',zip_folder,zip_filename+'.zip')
filename=zip_filename
dict_data={"result":"ok","msg":"文件压缩包下载链接获取成功","url":url,"filename":filename}
else:
url=os.path.join(code_relative_path,code_folder_path,treenode_path)
filename=os.path.basename(treenode_path)
dict_data={"result":"ok","msg":"文件下载链接获取成功","url":url,"filename":filename}
return HttpResponse(json.dumps(dict_data))
return HttpResponse(json.dumps({"result":"error","msg":"文件不存在"}))
return HttpResponse(json.dumps({"result":"error","msg":"请求参数错误"}))
return HttpResponse(json.dumps({"result":"error","msg":"请求方法不支持"}))
```
#### 配置django模板
创建模板目录
```bash
mkdir blog/templates/blog -p
```
创建基础模板
```bash
vim blog/templates/blog/base.html
```
##### 配置base.html
```html
<!DOCTYPE html>
{% load staticfiles %}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!--<meta name="viewport" content="width=device-width, initial-scale=1.0">-->
<meta name='viewport' content='width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0' />
<meta name='apple-mobile-web-app-capable' content='yes' />
<title>Ztree demo</title>
{% block css %}
<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
{% endblock %}
{% block javascript %}
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
{% block extra_javascript %}
{% endblock %}
</body>
</html>
```
```bash
vim blog/templates/blog/demo.html
```
```html
{% extends 'blog/base.html' %}
{% load staticfiles %}
{% block css %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'css/ztree/zTreeStyle.css' %}" type="text/css">
<style>
div#rMenu {position:absolute; visibility:hidden; top:0; text-align: left;padding: 1px;}
div#rMenu ul li{
margin: 0px 0;
padding: 0 5px;
cursor: pointer;
list-style: none outside none;
font-weight:bold;
background-color: #90CAF9;
font-family:"Microsoft YaHei";
}
//设置ztree显示样式
.highlight_red {color:#A60000;}
.highlight_green {color:#A7F43D;}
//li {list-style: circle;font-size: 12px;}
li.title {list-style: none;}
ul.list {margin-left: 17px;}
div.content_wrap {width: 600px;height:380px;}
div.content_wrap div.left{float: left;width: 250px;}
div.content_wrap div.right{float: right;width: 340px;}
div.zTreeDemoBackground {width:250px;height:362px;text-align:left;}
ul.ztree {margin-top: 0px;border: 1px solid #617775;background: #f0f6e4;width:220px;height:360px;overflow-y:scroll;overflow-x:auto;}
ul.ztree {margin-top: 0px;border: 1px solid #617775;background: #f0f6e4;overflow-y:scroll;overflow-x:auto;}
ul.log {border: 1px solid #617775;background: #f0f6e4;width:300px;height:170px;overflow: hidden;}
ul.log.small {height:45px;}
ul.log li {color: #666666;list-style: none;padding-left: 10px;}
ul.log li.dark {background-color: #E3E3E3;}
/* ruler */
div.ruler {height:20px; width:220px; background-color:#f0f6e4;border: 1px solid #333; margin-bottom: 5px; cursor: pointer}
div.ruler div.cursor {height:20px; width:30px; background-color:#3C6E31; color:white; text-align: right; padding-right: 5px; cursor: pointer}
.ztree li span.button.add {margin-left:2px; margin-right: -1px; background-position:-144px 0; vertical-align:top; *vertical-align:middle}
[hidden] {
display: none !important;
}
//配置图片div的显示区域
#yourimg_div {width:800px;height:600px;}
</style>
{% endblock %}
{% block javascript %}
{{ block.super }}
<script type="text/javascript" src="{% static 'js/ztree/jquery.ztree.core.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/ztree/jquery.ztree.exedit.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/ztree/custom-ztree.js' %}"></script>
{% endblock %}
{% block body %}
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<label class="btn btn-success btn-sm">
上传文件<input type="file" id="uploadFile" value="uploadFile" hidden>
</label>
<label class="btn btn-success btn-sm">
上传目录<input type="file" id="uploadFolder" value="uploadFolder" webkitdirectory directory multiple hidden>
</label>
<label class="btn btn-success btn-sm" id="downloadFile">
下载
</label>
</div>
<div class="col-md-2">
<div id="notice">
</div>
</div>
</div>
<div class="row">
<div class="col-md-2" id="treeDemo-parent-col">
<div id="filetree" class="content_wrap" style="width:100%;">
<div class="zTreeDemoBackground left" style="width:100%;">
<ul id="treeDemo" class="ztree" style="width:100%;"></ul>
</div>
</div>
</div>
<div class="col-md-5">
<div id="yourcode_div">
</div>
<div id="yourimg_div" style="display:none;width:800px;height:600px;" >
</div>
<button class="btn btn-primary" id="saveCode" type="button">保存</button>
</div>
</div>
</div>
{% csrf_token %}
<input id="codeFolderPath" value="{{code_folder_path}}" type="hidden">
<div id="rMenu">
<ul style="padding-left:0px;">
<li id="m_add_file" onclick="addTreeNode('file');">增加文件</li>
<li id="m_add_folder" onclick="addTreeNode('folder');">增加目录</li>
<li id="m_rename" onclick="renameTreeNode();">重命名</li>
<li id="m_del" onclick="removeTreeNode();">删除</li>
</ul>
</div>
{% endblock %}
{% block extra_javascript %}
<script type="text/javascript">
$(document).ready(function(){
//ztree
$.fn.zTree.init($("#treeDemo"), setting);
zTree = $.fn.zTree.getZTreeObj("treeDemo");
rMenu = $("#rMenu");
$("#uploadFile").bind("change",uploadFile);
$("#uploadFile").bind("click",beforeUploadFile);
$("#uploadFolder").bind("change",uploadFolder);
$("#uploadFolder").bind("click",beforeUploadFolder);
$("#downloadFile").bind("click",downloadFile);
$("#saveCode").bind("click",saveNode);
});
</script>
{% endblock %}
```
#### 创建ztree目录
```bash
mkdir myproject/static/{js,css}/ztree/ -p
```
#### 下载zTree
ztree.core(核心模块):https://gitee.com/zTree/zTree_v3/blob/master/js/jquery.ztree.core.min.js
ztree.exedit(exedit扩展模块):https://gitee.com/zTree/zTree_v3/blob/master/js/jquery.ztree.exedit.min.js
ztree css:https://gitee.com/zTree/zTree_v3/blob/master/css/zTreeStyle/zTreeStyle.css
ztree 图片:https://gitee.com/zTree/zTree_v3/tree/master/css/zTreeStyle/img
js文件放到 `myproject/static/js/ztree`目录
css文件放到 `myproject/static/css/ztree`目录
img文件夹放到 `myproject/static/css/ztree`目录
#### 创建自定义js文件
```bash
vim myproject/static/js/ztree/custom-ztree.js
```
```javascript
var setting = {
async: {
enable: true,
url: '/blog/async_get_nodes/',
otherParam: getOtherParam,
},
view: {
selectedMulti: false,
showLine: false,
expandSpeed: "slow"
},
edit: {
drag:{
isCopy: false,
isMove: true,
prev: true,
next: true,
inner: true
},
enable: true,
editNameSelectAll: true,
showRemoveBtn: false,
showRenameBtn: false,
},
data: {
keep: {
leaf: true,
parent: true
},
simpleData: {
enable: true
}
},
callback: {
beforeExpand: beforeExpand,
beforeDrag: beforeDrag,
beforeDrop: beforeDrop,
beforeClick: beforeClick,
beforeRename: beforeRename,
onRightClick: OnRightClick,
onDblClick: OnDblClick,
onClick: OnClick
}
};
function showNotice(type,msg) {
if(type=="ok"){
$('#notice').html('<span class="glyphicon glyphicon-ok-sign"></span> '+msg);
$('#notice').css('color', 'green');
}else if(type=="error"){
$('#notice').html('<span class="glyphicon glyphicon-remove-sign"></span> '+msg);
$('#notice').css('color', 'red');
}else if(type=="hidden"){
$('#notice').html('');
}
}
function getOtherParam(treeId, treeNode){
var codeFolderPath=$("#codeFolderPath").val();
var token=$('input[name=csrfmiddlewaretoken]').val();
var treeNodePath=getTreeNodePath(treeNode);
return {'codeFolderPath':codeFolderPath,'treeNodePath':treeNodePath,'csrfmiddlewaretoken':token}
}
function getTreeNodePath(treeNode){
if(treeNode == null){
return '';
}
var ParentNode=treeNode.getParentNode();
if(ParentNode==null){
return treeNode.name;
}
return getTreeNodePath(ParentNode)+"/"+treeNode.name;
}
function beforeExpand(treeId, treeNode) {
if (!treeNode.isAjaxing) {
startTime = new Date();
treeNode.times = 1;
ajaxGetNodes(treeNode, "refresh");
return true;
} else {
showNotice("ok","加载数据中,请稍后...");
return false;
}
}
function ajaxGetNodes(treeNode, reloadType) {
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
showNotice("ok","数据加载中...");
if (reloadType == "refresh") {
zTree.updateNode(treeNode);
}
zTree.reAsyncChildNodes(treeNode, reloadType, true,function(){
showNotice("ok","数据加载完成");
});
}
function beforeDrag(treeId, treeNodes) {
for (var i=0,l=treeNodes.length; i<l; i++) {
if (treeNodes[i].drag === false) {
return false;
}
}
return true;
}
function beforeDrop(treeId, treeNodes, targetNode, moveType) {
ajaxDragNode(treeNodes[0],targetNode);
setTimeout(function(){
zTree.reAsyncChildNodes(targetNode, "refresh", false);
},100);
return targetNode ? targetNode.drop !== false : true;
}
function ajaxDragNode(treeNode,targetNode){
var codeFolderPath=$("#codeFolderPath").val();
var token=$('input[name=csrfmiddlewaretoken]').val();
var srcTreeNodePath=getTreeNodePath(treeNode);
var dstTreeNodePath=getTreeNodePath(targetNode);
$.post('/blog/move_node/', { codeFolderPath: codeFolderPath, srcTreeNodePath: srcTreeNodePath, dstTreeNodePath: dstTreeNodePath, csrfmiddlewaretoken: token},
function(returnedData){
var obj = jQuery.parseJSON(returnedData);
if(obj.hasOwnProperty('result')){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
console.log(obj.msg);
if(obj.result == "ok"){
showNotice("ok",'节点移动完成');
return true;
}
showNotice('error','节点移动失败');
return false;
}
}).fail(function(){
showNotice('error','节点移动异常,请联系管理员');
return false;
});
}
function OnDblClick(event, treeId, treeNode) {
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.cancelSelectedNode();
showNotice("ok",'已选中根目录');
}
function OnClick(event, treeId, treeNode) {
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
var nodes = zTree.getSelectedNodes();
var SelectedTreeNode = nodes[0];
if(SelectedTreeNode.isParent){
showNotice("ok",'已选中目录');
}
}
function OnRightClick(event, treeId, treeNode) {
if (!treeNode && event.target.tagName.toLowerCase() != "button" && $(event.target).parents("a").length == 0) {
zTree.cancelSelectedNode();
showRMenu("root", event.clientX, event.clientY);
} else if (treeNode && !treeNode.noR) {
zTree.selectNode(treeNode);
if(treeNode.isParent){
showRMenu("folder", event.clientX, event.clientY);
}else{
showRMenu("file", event.clientX, event.clientY);
}
}
}
function showRMenu(type, x, y) {
$("#rMenu ul").show();
if (type=="file") {
$("#m_del").show();
$("#m_edit").show();
$("#m_rename").show();
$("#m_add_folder").hide();
$("#m_add_file").hide();
} else if (type=="folder") {
$("#m_del").show();
$("#m_edit").hide();
$("#m_rename").show();
$("#m_add_file").show();
$("#m_add_folder").show();
}else{
$("#m_del").hide();
$("#m_edit").hide();
$("#m_rename").hide();
$("#m_add_file").show();
$("#m_add_folder").show();
}
y += document.body.scrollTop;
x += document.body.scrollLeft;
rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
$("body").bind("mousedown", onBodyMouseDown);
}
function hideRMenu() {
if (rMenu) rMenu.css({"visibility": "hidden"});
$("body").unbind("mousedown", onBodyMouseDown);
}
function onBodyMouseDown(event){
if (!(event.target.id == "rMenu" || $(event.target).parents("#rMenu").length>0)) {
rMenu.css({"visibility" : "hidden"});
}
}
function addTreeNode(fileType) {
hideRMenu();
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
var nodes = zTree.getSelectedNodes();
var SelectedTreeNode = nodes[0];
zTree.reAsyncChildNodes(SelectedTreeNode, "refresh", false,function(){
handlerAddTreeNode(fileType);
});
}
function handlerAddTreeNode(fileType){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
var nodes = zTree.getSelectedNodes();
var SelectedTreeNode = nodes[0];
var isParent = false;
var tempName = "NewFile";
if(fileType == "folder"){
isParent = true;
tempName = "NewFolder";
}
tempName=MakeUniqueNodeName(SelectedTreeNode,fileType);
if (SelectedTreeNode) {
NewTreeNode = zTree.addNodes(SelectedTreeNode, {name: tempName, isParent: isParent});
} else {
NewTreeNode = zTree.addNodes(null, {name: tempName, isParent: isParent} );
}
ajaxAddNode(SelectedTreeNode,fileType,NewTreeNode[0].name);
zTree.editName(NewTreeNode[0]);
}
function filter(Node){
return Node.level == 0;
}
function filterChildNode(Node){
return true;
}
function nameExistNode(NodeList,Name){
for(i=0;i<NodeList.length;i++){
if(NodeList[i].name == Name){
return true;
}
}
return false;
}
function MakeUniqueNodeName(TreeNode,fileType){
var tempName = "NewFile";
if(fileType == "folder"){
tempName = "NewFolder";
}
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
if(TreeNode){
var NodesList=zTree.getNodesByFilter(filterChildNode,false,TreeNode);
}else{
var NodesList=zTree.getNodesByFilter(filter);
}
var counter=1;
var NewTempName=tempName;
while(nameExistNode(NodesList,NewTempName)){
NewTempName=tempName+counter;
counter++;
}
return NewTempName;
}
function ajaxAddNode(treeNode,fileType,newName){
var codeFolderPath=$("#codeFolderPath").val();
var token=$('input[name=csrfmiddlewaretoken]').val();
var treeNodePath=getTreeNodePath(treeNode);
$.post('/blog/add_node/', { codeFolderPath: codeFolderPath, treeNodePath: treeNodePath, newName: newName ,fileType: fileType, csrfmiddlewaretoken: token},
function(returnedData){
var obj = jQuery.parseJSON(returnedData);
if(obj.hasOwnProperty('result')){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
console.log(obj.msg);
if(obj.result == "ok"){
showNotice("ok","新建节点完成");
return true;
}
showNotice("error","新建节点请求失败");
return false;
}
}).fail(function(){
showNotice("error","新建节点请求异常,请联系管理员");
return false;
});
}
function removeTreeNode() {
hideRMenu();
var nodes = zTree.getSelectedNodes();
if (nodes && nodes.length>0) {
if (nodes[0].children && nodes[0].children.length > 0) {
var msg = "要删除的节点是父节点,如果删除将连同子节点一起删掉。\n\n请确认!";
if (confirm(msg)==true){
ajaxRemoveNode(nodes[0].getParentNode(),nodes[0].name);
zTree.reAsyncChildNodes(nodes[0],"refresh",false);
}
} else {
ajaxRemoveNode(nodes[0].getParentNode(),nodes[0].name);
zTree.reAsyncChildNodes(nodes[0],"refresh",false);
}
}
}
function ajaxRemoveNode(treeNode,name){
var codeFolderPath=$("#codeFolderPath").val();
var token=$('input[name=csrfmiddlewaretoken]').val();
var treeNodePath=getTreeNodePath(treeNode);
$.post('/blog/remove_node/', { codeFolderPath: codeFolderPath, treeNodePath: treeNodePath, name: name , csrfmiddlewaretoken: token},
function(returnedData){
var obj = jQuery.parseJSON(returnedData);
if(obj.hasOwnProperty('result')){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.reAsyncChildNodes(treeNode,"refresh",false);
console.log(obj.msg);
if(obj.result == "ok"){
showNotice("ok","删除节点完成");
return true;
}
showNotice("error","删除节点失败");
return false;
}
}).fail(function(){
showNotice("error","删除异常异常,请联系管理员");
return false;
});
}
function beforeRename(treeId, treeNode, newName) {
if (newName.length == 0) {
showNotice("error","节点名称不能为空");
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
setTimeout(function(){zTree.editName(treeNode)}, 10);
return false;
}
ajaxRenameNode(treeNode,newName);
return true;
}
function ajaxRenameNode(treeNode,newName){
var codeFolderPath=$("#codeFolderPath").val();
var token=$('input[name=csrfmiddlewaretoken]').val();
var treeNodePath=getTreeNodePath(treeNode);
if(treeNode.name == newName ){
return true;
}
$.post('/blog/rename_node/', { codeFolderPath: codeFolderPath, treeNodePath: treeNodePath, newName: newName , csrfmiddlewaretoken: token},
function(returnedData){
var obj = jQuery.parseJSON(returnedData);
if(obj.hasOwnProperty('result')){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.reAsyncChildNodes(treeNode.getParentNode(),"refresh",false);
if(obj.result == "ok"){
showNotice("ok",'重命名节点完成');
return true;
}
showNotice('error','重命名节点失败');
return false;
}
}).fail(function(){
showNotice('error','重命名节点异常,请联系管理员');
return false;
});
}
function renameTreeNode() {
hideRMenu();
var zTree = $.fn.zTree.getZTreeObj("treeDemo"),
nodes = zTree.getSelectedNodes(),
treeNode = nodes[0];
if (nodes.length == 0) {
showNotice("error","请先选择一个节点");
return;
}
zTree.editName(treeNode);
};
function utf8_to_b64(str) {
return window.btoa(unescape(encodeURIComponent( str )));
}
function b64_to_utf8(str) {
return decodeURIComponent(escape(window.atob( str )));
}
function isImage(filename)
{
var flag = false;
var arr = ["jpg","png","gif"];
var index = filename.lastIndexOf(".");
var ext = filename.substr(index+1);
for(var i=0;i<arr.length;i++)
{
if(ext == arr[i])
{
flag = true;
break;
}
}
return flag;
}
function beforeClick(treeId, treeNode,clickFlag) {
if(!treeNode.isParent){
if(isImage(treeNode.name)){
var codeFolderPath=$("#codeFolderPath").val();
var treeNodePath=getTreeNodePath(treeNode);
var codeRootPath='upload/pygame/code_folder_path';
var image_url='/'+codeRootPath+'/'+codeFolderPath+'/'+treeNodePath;
var elem = document.createElement("img");
elem.setAttribute("src", image_url);
elem.setAttribute("style", "margin:auto;");
elem.setAttribute("alt", "Flower");
document.getElementById("yourcode_div").style.display="none";
document.getElementById("yourimg_div").innerHTML = "";
document.getElementById("yourimg_div").style.background="url("+image_url+") center no-repeat";
document.getElementById("yourimg_div").style.display="";
showNotice("ok","图片加载完成");
}else{
document.getElementById("yourcode_div").style.display="";
document.getElementById("yourimg_div").style.display="none";
ajaxReadNode(treeNode);
}
}
return true;
}
function ajaxReadNode(treeNode){
var codeFolderPath=$("#codeFolderPath").val();
var token=$('input[name=csrfmiddlewaretoken]').val();
var treeNodePath=getTreeNodePath(treeNode);
$.post('/blog/read_node/', { codeFolderPath: codeFolderPath, treeNodePath: treeNodePath, csrfmiddlewaretoken: token},
function(returnedData){
var obj = jQuery.parseJSON(returnedData);
if(obj.hasOwnProperty('result')){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
if(obj.result == "ok"){
var base64_enc_content=obj.data;
if(base64_enc_content == ""){
$('#yourcode_div').text("");
}else{
$('#yourcode_div').text(b64_to_utf8(base64_enc_content));
}
showNotice("ok","读取节点完成");
return true;
}
$('#yourcode_div').text("");
showNotice("error","读取节点失败,二进制格式无法读取");
return false;
}
}).fail(function(){
showNotice("error","读取节点异常,请联系管理员");
return false;
});
}
function saveNode(){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
var nodes = zTree.getSelectedNodes();
var SelectedTreeNode = nodes[0];
if(nodes[0].isParent){
return;
}
ajaxsaveNode(SelectedTreeNode);
}
function ajaxsaveNode(treeNode){
var codeFolderPath=$("#codeFolderPath").val();
var token=$('input[name=csrfmiddlewaretoken]').val();
var treeNodePath=getTreeNodePath(treeNode);
var yourcode=$('#yourcode_div').text();
var encodedFileContent=utf8_to_b64(yourcode);
$.post('/blog/save_node/', { codeFolderPath: codeFolderPath, treeNodePath: treeNodePath,encodedFileContent:encodedFileContent, csrfmiddlewaretoken: token},
function(returnedData){
var obj = jQuery.parseJSON(returnedData);
if(obj.hasOwnProperty('result')){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
if(obj.result == "ok"){
showNotice("ok","节点保存完成");
return true;
}
showNotice("error","节点保存失败");
return false;
}
}).fail(function(){
showNotice("error","节点保存异常,请联系管理员");
return false;
});
}
function uploadFile(){
console.log('uploadFile');
var FileMaxSize=10*1024*1024;
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
var nodes = zTree.getSelectedNodes();
zTree.reAsyncChildNodes(nodes[0], "refresh", false);
if(nodes.length != 0 && !nodes[0].isParent){
showNotice("error","请选择目录");
return;
}
var treeNodePath=getTreeNodePath(nodes[0]);
var codeFolderPath=$("#codeFolderPath").val();
var token=$('input[name=csrfmiddlewaretoken]').val();
var uploadFile=$('#uploadFile')[0].files[0];
if ( uploadFile.size > FileMaxSize){
showNotice('error','上传文件最大限制为'+(FileMaxSize/1024/1024).toFixed(2)+'M,'+uploadFile.name+':'+(uploadFile.size/1024/1024).toFixed(2)+'M');
return false;
}
var formData=new FormData();
formData.append("codeFolderPath",codeFolderPath);
formData.append("treeNodePath",treeNodePath);
formData.append("csrfmiddlewaretoken",token);
formData.append("uploadFile",uploadFile);
$.ajax({
url: '/blog/upload_file/',
data: formData,
type: 'POST',
contentType: false,
processData: false,
success(returnedData){
var obj = jQuery.parseJSON(returnedData);
if(obj.result == "ok"){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
var nodes = zTree.getSelectedNodes();
zTree.reAsyncChildNodes(nodes[0], "refresh", false,function(){
showNotice("ok",'文件上传完成');
var upload_node = zTree.getNodeByParam("id",obj.id,nodes[0]);
zTree.selectNode(upload_node);
ajaxReadNode(upload_node);
});
}else{
showNotice('error',obj.msg);
}
document.getElementById("uploadFile").value = "";
},
error(err){
showNotice('error','文件上传失败');
console.log(err);
}
});
}
function downloadFile(){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
var nodes = zTree.getSelectedNodes();
var treeNodePath=getTreeNodePath(nodes[0]);
var codeFolderPath=$("#codeFolderPath").val();
var token=$('input[name=csrfmiddlewaretoken]').val();
$.post('/blog/download_node/', { codeFolderPath: codeFolderPath, treeNodePath: treeNodePath, csrfmiddlewaretoken: token},
function(returnedData){
var obj = jQuery.parseJSON(returnedData);
if(obj.hasOwnProperty('result')){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
if(obj.result == "ok"){
showNotice("ok","下载链接获取成功");
var a = document.createElement('a');
var url = obj.url;
a.href = window.location.protocol+'//'+window.location.host+'/'+url;
a.download = obj.filename;
a.click();
return true;
}
showNotice("error","下载节点失败");
return false;
}
}).fail(function(){
showNotice("error","下载节点异常,请联系管理员");
return false;
});
}
function uploadFolder(){
var FolderMaxNumber=1000;
var FolderMaxSize=50*1024*1024;
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
var nodes = zTree.getSelectedNodes();
zTree.reAsyncChildNodes(nodes[0], "refresh", false);
if(nodes.length != 0 && !nodes[0].isParent){
showNotice("error","请选择目录");
return;
}
var treeNodePath=getTreeNodePath(nodes[0]);
var codeFolderPath=$("#codeFolderPath").val();
var token=$('input[name=csrfmiddlewaretoken]').val();
var uploadFiles=$('#uploadFolder')[0].files;
if(uploadFiles.length>FolderMaxNumber){
showNotice('error','上传文件夹包含的文件数限制为'+FolderMaxNumber+',当前文件数:'+uploadFiles.length);
return;
}
var uploadFilesTotalSize=0;
for(var i=0;i<uploadFiles.length;i++){
uploadFilesTotalSize+=uploadFiles[i].size
}
if (uploadFilesTotalSize>FolderMaxSize){
showNotice('error','上传文件夹包含的文件总大小为'+(FolderMaxSize/1024/1024).toFixed(2)+'M,当前文件总大小:'+(uploadFilesTotalSize/1024/1024).toFixed(2)+'M');
return;
}
var formData=new FormData();
for(var i=0;i<uploadFiles.length;i++){
formData.append("uploadFiles",uploadFiles[i]);
formData.append("uploadFilePaths",uploadFiles[i]['webkitRelativePath']);
}
formData.append("codeFolderPath",codeFolderPath);
formData.append("treeNodePath",treeNodePath);
formData.append("csrfmiddlewaretoken",token);
$.ajax({
url: '/blog/upload_folder/',
data: formData,
type: 'POST',
contentType: false,
processData: false,
success(returnedData){
var obj = jQuery.parseJSON(returnedData);
if(obj.hasOwnProperty('id')){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
var upload_node = zTree.getNodeByParam("id",obj.id,nodes[0]);
if(obj.result == "ok"){
setTimeout(function(){
zTree.reAsyncChildNodes(upload_node, "refresh", false,function(){
zTree.selectNode(upload_node);
showNotice("ok",obj.msg);
});
},100);
}else{
setTimeout(function(){
zTree.reAsyncChildNodes(upload_node, "refresh", false,function(){
zTree.selectNode(upload_node);
showNotice('error',"警告,部分文件上传失败!");
setTimeout(function(){
alert(obj.msg);
},300);
});
},100);
}
}else{
showNotice('error',"警告,所有文件上传失败!");
setTimeout(function(){
alert(obj.msg);
},300);
}
document.getElementById("uploadFolder").value = "";
},
error(err){
showNotice('error','警告,文件夹上传失败');
document.getElementById("uploadFolder").value = "";
}
});
}
function beforeUploadIsReady(){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
var nodes = zTree.getSelectedNodes();
if(nodes.length != 0 && !nodes[0].isParent){
showNotice("error","请选择目录上传,选择根目录请双击空白处");
return false;
}
return true;
}
function beforeUploadFile(){
if(!beforeUploadIsReady()){
event.preventDefault();
}
}
function beforeUploadFolder(){
if(!beforeUploadIsReady()){
event.preventDefault();
}
}
```
### demo

### 实例
https://ynotes.cn/blog/pygame_v2/