前端:
<el-upload
:before-remove="beforeRemove"
:file-list="fileList"
:limit="1"
:on-exceed="handleExceed"
:on-success="handleAvatarSuccess"
accept=".json"
action="http://127.0.0.1:8000/crud/"
class="upload-demo"
method="POST"
name="jsonfile"
style="margin-top: 50px"
>
<el-button round size="large" type="primary">点击上传</el-button>
<template #tip>
<div class="el-upload__tip" style="font-size: 15px">只能上传 json 文件,建议不超过 1M</div>
</template>
</el-upload>
<div style="display: flex;justify-content: space-between">
<el-button round size="large" type="danger" @click="submit">确定提交</el-button>
<el-link :underline="false" download href="http://127.0.0.1:8000/download_json/" style="border-radius: 50px;">
<el-button round size="large" type="success">下载文件</el-button>
</el-link>
</div>
<div class="view">
<el-input v-model="filterText" placeholder="输入关键字进行过滤"></el-input>
<el-tree
ref="tree"
:data="data"
:expand-on-click-node="false"
:filter-node-method="filterNode"
class="filter-tree"
draggable
node-key="id"
>
<!-- default-expand-all-->
<template #default="{ node, data }">
<span class="custom-tree-node">
<span>{{ node.label }}</span>
<span>
<el-button style="font-size: 18px" type="text" @click="append(data)">
<CirclePlus style="width: 1em; height: 1em;"/>
</el-button>
<el-button style="font-size: 18px" type="text" @click="remove(node, data)">
<Delete style="width: 1em; height: 1em;"/>
</el-button>
<el-button style="font-size: 18px" type="text" @click="change(data);">
<EditPen style="width: 1em; height: 1em;"/>
</el-button>
</span>
</span>
</template>
</el-tree>
</div>
文件上传成功调用接口返回数据:(需要使用push添加到data)
async handleAvatarSuccess() {
console.log(this.fileList)
const {data: res} = await this.$http.get(`http://127.0.0.1:8000/crud_json/`)
console.log(res)
this.data = []
this.data.push(res.tree)
this.id = res.size
},
增加节点以及修改节点使用elementui-plus 消息弹框组件
MessageBox 消息弹框 | Element Plus (gitee.io)
增:
append(data) {
console.log(data)
let id = this.id
this.$prompt('请输入内容', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
})
.then(({value}) => {
const newChild = {id: this.id++, label: value, children: []}
if (!data.children) {
data.children = []
}
data.children.unshift(newChild)
this.data = [...this.data]
console.log(id)
})
.catch(() => {
this.$message({
type: 'info',
message: '取消输入',
})
})
},
删:
remove(node, data) {
const parent = node.parent
const children = parent.data.children || parent.data
const index = children.findIndex((d) => d.id === data.id)
children.splice(index, 1)
this.data = [...this.data]
},
改:
change(data) {
this.$prompt('请输入更改内容', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
})
.then(({value}) => {
data.label = value
})
.catch(() => {
this.$message({
type: 'info',
message: '取消输入',
})
})
},
查:
filterNode(value, data) {
if (!value) return true
return data.label.indexOf(value) !== -1
},
注:提供给树形列表的数据需要有id值,不然删除节点时会删错
提交:
async submit() {
const tree = JSON.stringify(this.data)
var axios = require('axios');
var config = {
method: 'post',
url: 'http://127.0.0.1:8000/crud_get/',
headers: {
'Content-Type': 'application/json'
},
data: tree
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
},
注:推荐使用post方法,数据过多时get字符串拼接方式会报错 414 message Request-URL Too Long
下载:
<el-link download href="http://127.0.0.1:8000/download_json/">
<el-button>下载文件</el-button>
</el-link>
后端:
def crud(request):
# 接收用户上传json
if request.method == 'POST':
image = request.FILES.get('jsonfile')
if image.name.split('.')[-1] in ['json']:
# 这里的file_path指的是服务器上保存图片的路径
file_path = settings.JSON_ROOT + image.name[-10:]
file_path = default_storage.save(file_path, image)
path_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
return JsonResponse({
'success': True,
# 这里的file_path指的是访问该图片的url
# 'file_path': settings.MEDIA_URL + file_path.split('/')[-1],
'file_path': path_root + settings.JSON_URL + file_path.split('/')[-1],
'msg': 'Success!'
})
else:
return JsonResponse({
'success': False,
'file_path': '',
'msg': 'Unexpected File Format'
})
else:
raise PermissionDenied('Only Accept POST Request!')
接收用户上传的json文件保存到服务器
如果文件上传成功后调用接口返回el树形列表需要的data:
def crud_json(request):
# 从后端返回json
file_dir = r"E:\Code\PycharmProjects\test718\media\json"
file_dict = {}
lists = os.listdir(file_dir) # 先获取文件夹内的所有文件
for i in lists: # 遍历所有文件
ctime = os.stat(os.path.join(file_dir, i)).st_ctime
file_dict[ctime] = i # 添加创建时间和文件名到字典
max_ctime = max(file_dict.keys()) # 取值最大的时间
print(file_dict[max_ctime]) # 打印出最新文件名
a = file_dict[max_ctime]
json_path = os.path.join(file_dir, a)
with open(json_path, encoding='utf-8') as f:
res = json.load(f)
# print(res)
res = json.dumps(res)
# print(res)
return HttpResponse(res)
注:遍历最新上传的文件返回给前端
前端提交后后端拿到el树形列表data调用方法(获取总结点个数用于第二次使用)保存到固定json文件内:
class ResetTreeId:
def __init__(self):
return
def reset_tree_ids(self, dic):
"""
:param dic:
:return:
"""
dic["id"] = self.reset_id
self.reset_id += 1
if "children" not in dic:
return dic
tmp_children = []
for nt in dic["children"]:
tmp_children.append(self.reset_tree_ids(nt))
dic["children"] = tmp_children
return dic
def reset(self, js):
self.reset_id = 1
# 中括号
dic = self.reset_tree_ids(js["tree"][0])
js = {"size": self.reset_id, "tree": dic}
self.reset_id = 1
return js
RTI = ResetTreeId()
def crud_get(request):
# 保存修改后json文件
data = request.body.decode()
tree = json.loads(data)
jsonfile = {"size": 1000, "tree": tree}
jsonfile = RTI.reset(jsonfile)
jsonfile = json.dumps(jsonfile, indent=4, ensure_ascii=False)
# print(json)
json_path = os.path.join(root_dir, "data/result.json")
with open(json_path, "w", encoding='utf-8') as f:
f.write(jsonfile)
return HttpResponse('保存成功')
前端点击下载后返回给前端二进制文件流:
def read_file(file_name, chunk_size=512):
with open(file_name, "rb") as f:
while True:
c = f.read(chunk_size)
if c:
yield c
else:
break
def download_json(request):
# 下载Json
file_path = os.path.join(root_dir, "data/result.json")
file_name = "result.json"
response = StreamingHttpResponse(read_file(file_path))
response["Content-Type"] = "application/octet-stream"
response["Content-Disposition"] = 'attachment; filename={0}'.format(file_name)
response["Access-Control-Expose-Headers"] = "Content-Disposition" # 为了使前端获取到Content-Disposition属性
return response
避坑:
el树形列表数据必须要有id值