Commit 9e6639ab authored by wanli's avatar wanli

feat(文件管理模块): 完善文件管理增删查改功能

parent 0aa0cd91
'''
Author: your name
Date: 2021-06-30 18:03:41
LastEditTime: 2021-07-17 17:50:02
LastEditTime: 2021-07-17 21:24:19
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath: \evm-store\tools\build_out\application\signal_manager.py
......@@ -59,6 +59,10 @@ class SignalManager(object):
actionGetFilePreview = PySignal()
actionPostFileUpdate = PySignal()
actionPostFileUpload = PySignal()
actionPostFileDelete = PySignal()
actionPostFileCreate = PySignal()
actionPostFileCreateDir = PySignal()
actionPostFilePaste = PySignal()
def __init__(self):
super().__init__()
......
'''
Author: your name
Date: 2021-06-30 17:43:46
LastEditTime: 2021-07-17 17:54:09
LastEditTime: 2021-07-17 21:25:21
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath: \evm-store\tools\build_out\controllers\__init__.py
......@@ -72,3 +72,7 @@ def initConnect():
signalManager.actionGetFileTree.connect(fileManager.tree)
signalManager.actionPostFileUpdate.connect(fileManager.update_file)
signalManager.actionPostFileUpload.connect(fileManager.upload)
signalManager.actionPostFileDelete.connect(fileManager.delete)
signalManager.actionPostFileCreate.connect(fileManager.create_file)
signalManager.actionPostFileCreateDir.connect(fileManager.create_dir)
signalManager.actionPostFilePaste.connect(fileManager.paste)
import pprint
import base64
import shutil
from pathlib import Path
import json
import mimetypes
......@@ -180,7 +181,7 @@ class FileManager(object):
def disk(self, disk):
# select-disks
logger.info(disk)
print(disk)
return True, ResponseCode.HTTP_SUCCESS
def update_file(self, disk, path, file):
......@@ -199,39 +200,106 @@ class FileManager(object):
file.save(target_file.resolve().as_posix())
return True, ResponseCode.HTTP_SUCCESS
def upload(self, disk):
def upload(self, disk, path, overwrite, fileList):
# upload
logger.info(disk)
print(disk)
target_dir = Path(disk_root).joinpath(disk).joinpath(path)
if not target_dir.exists():
target_dir.mkdir()
for file in fileList:
target_file = target_dir.joinpath(file.filename)
if target_file.exists() and overwrite == 0:
continue
target_file.unlink()
file.save(target_file.resolve().as_posix())
return True, ResponseCode.HTTP_SUCCESS
def create_dir(self, disk, target_file):
def create_dir(self, disk, path, name):
# create directory
logger.info(disk, target_file)
print(disk, path, name)
target_dir = Path(disk_root).joinpath(disk).joinpath(path).joinpath(name)
if not target_dir.exists():
target_dir.mkdir()
return True, ResponseCode.HTTP_SUCCESS
def create_file(self, disk, target_file, content):
def create_file(self, disk, path, name):
# create file
logger.info(disk, target_file, content)
print(disk, disk, path, name)
target_dir = Path(disk_root).joinpath(disk).joinpath(path)
if not target_dir.exists():
target_dir.mkdir()
target_file = target_dir.joinpath(name)
if target_file.exists():
return False, ResponseCode.FILE_EXISTS
target_file.touch()
return True, ResponseCode.HTTP_SUCCESS
def delete(self, disk):
def delete(self, disk, items):
# delete file
logger.info(disk)
print(disk, items, type(items))
for item in items:
target = Path(disk_root).joinpath(disk).joinpath(item.get("path"))
if not target.exists():
return False, ResponseCode.APPLICATION_NOT_EXISTS
if target.is_dir():
target.mkdir()
elif target.is_file():
target.unlink()
return True, ResponseCode.HTTP_SUCCESS
def copy(self, disk):
# copy file
logger.info(disk)
# 无动作,前端缓存相关变量值
print(disk)
return True, ResponseCode.HTTP_SUCCESS
def cut(self, disk):
# cut file
logger.info(disk)
# 复制源文件路径
# 无动作,前端缓存相关变量值
print(disk)
return True, ResponseCode.HTTP_SUCCESS
def paste(self, disk):
def paste(self, disk, path, clipboard):
# paste file
logger.info(disk)
'''
directories: []
disk: "files"
files: ["code/config.ini"]
type: "copy"
directories: []
disk: "files"
files: ["code/alfa.sql"]
type: "cut"
'''
# 粘贴到当前目录,然后删除源文件
# 需要区分是复制操作还是剪贴操作
print(disk, path, clipboard)
target_dir = Path(disk_root).joinpath(disk).joinpath(path)
for dir in clipboard.get("directories", []):
source_dir = Path(disk_root).joinpath(disk).joinpath(dir)
if not source_dir.exists():
continue
if target_dir.joinpath(dir).exists():
continue
shutil.copytree(source_dir.resolve().as_posix(), target_dir.resolve().as_posix())
if clipboard.get("type") == "cut":
source_dir.rmdir()
for file in clipboard.get("files", []):
source_file = Path(disk_root).joinpath(disk).joinpath(file)
if not source_file.exists():
continue
if target_dir.joinpath(file).exists():
continue
shutil.copyfile(source_file.resolve().as_posix(), target_dir.resolve().as_posix())
if clipboard.get("type") == "cut":
source_dir.unlink()
return True, ResponseCode.HTTP_SUCCESS
def download(self, disk, target_file):
......@@ -246,7 +314,7 @@ class FileManager(object):
# with open(target_file.resolve().as_posix(), "r", encoding="utf-8") as f:
# data = f.read()
# logger.info(data)
# print(data)
mime = mimetypes.guess_type(target_file.resolve().as_posix())[0]
content = target_file.read_text(encoding="utf-8")
......
{"directories": [{"basename": "evueapps", "dirname": ".", "path": "evueapps", "timestamp": 1618901644, "type": "dir"}], "files": [{"basename": "appjs.c", "dirname": ".", "extension": "c", "filename": "appjs", "path": "appjs.c", "size": 2145, "timestamp": 1625480571, "type": "file"}, {"basename": "codefresh-build-3.yaml", "dirname": ".", "extension": "yaml", "filename": "codefresh-build-3", "path": "codefresh-build-3.yaml", "size": 268, "timestamp": 1626516235, "type": "file"}, {"basename": "result - \u526f\u672c.json", "dirname": ".", "extension": "json", "filename": "result - \u526f\u672c", "path": "result - \u526f\u672c.json", "size": 302, "timestamp": 1626518687, "type": "file"}, {"basename": "result.json", "dirname": ".", "extension": "json", "filename": "result", "path": "result.json", "size": 407, "timestamp": 1626520437, "type": "file"}]}
\ No newline at end of file
{"directories": [{"basename": "evueapps", "dirname": ".", "path": "evueapps", "timestamp": 1618901644, "type": "dir"}], "files": [{"basename": "appjs.c", "dirname": ".", "extension": "c", "filename": "appjs", "path": "appjs.c", "size": 2145, "timestamp": 1625480571, "type": "file"}, {"basename": "codefresh-build-3.yaml", "dirname": ".", "extension": "yaml", "filename": "codefresh-build-3", "path": "codefresh-build-3.yaml", "size": 268, "timestamp": 1626516235, "type": "file"}, {"basename": "result - \u526f\u672c.json", "dirname": ".", "extension": "json", "filename": "result - \u526f\u672c", "path": "result - \u526f\u672c.json", "size": 376, "timestamp": 1626524753, "type": "file"}, {"basename": "result.json", "dirname": ".", "extension": "json", "filename": "result", "path": "result.json", "size": 407, "timestamp": 1626520437, "type": "file"}]}
\ No newline at end of file
'''
Author: your name
Date: 2021-07-15 03:22:19
LastEditTime: 2021-07-17 17:54:50
LastEditTime: 2021-07-17 21:49:04
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath: \evm-store\tools\build_out\views\__init__.py
......@@ -59,6 +59,10 @@ api.add_resource(file.FileUpdate, "/file-manager/update-file")
api.add_resource(file.FileUpload, "/file-manager/upload")
api.add_resource(file.FileDownload, "/file-manager/download")
api.add_resource(file.FilePrview, "/file-manager/preview")
api.add_resource(file.FileCreateDir, "/file-manager/create-directory")
api.add_resource(file.FileCreate, "/file-manager/create-file")
api.add_resource(file.FilePaste, "/file-manager/paste")
api.add_resource(file.FileDelete, "/file-manager/delete")
api.add_resource(monitorWatch.MonitorWatchResource, '/monitorWatch/<string:uuid>')
api.add_resource(monitorWatch.MonitorWatchResourceList, '/monitorWatch')
......
......@@ -22,6 +22,7 @@ class AppResourceList(Resource):
@jwt_required(locations=["headers"])
def get(self):
# 特殊参数,即不是从json获取参数的接口,可以将这个注释打开
self.parser.add_argument('User-Agent', location='headers')
self.parser.add_argument("app", type=str, location="args", nullable=True, required=False)
self.parser.add_argument("scope", type=str, location="args", nullable=True, required=False)
self.parser.add_argument("app_name", type=str, location="args", nullable=True, required=False)
......@@ -57,6 +58,7 @@ class AppResourceList(Resource):
@jwt_required(locations=["headers"])
def post(self):
self.parser.add_argument('User-Agent', location='headers')
self.parser.add_argument("app_name", type=str, location="form", required=True)
self.parser.add_argument("app_version", type=str, location="form", required=True)
self.parser.add_argument("category", type=str, location="form", required=True)
......
'''
Author: your name
Date: 2021-07-09 12:39:40
LastEditTime: 2021-07-17 19:56:04
LastEditTime: 2021-07-17 22:17:51
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath: \evm-store\tools\build_out\views\file.py
'''
import traceback
from pathlib import Path
from flask import current_app, jsonify, request, make_response, Response
from flask_restful import Resource
from flask_restful import Resource, fields
from marshmallow import Schema, fields
from werkzeug.datastructures import FileStorage
from flask_restful.reqparse import RequestParser
from flask_jwt_extended import ( jwt_required, get_jwt_identity )
......@@ -112,16 +114,18 @@ class FileUpdate(Resource):
self.parser = RequestParser()
def post(self):
self.parser.add_argument("disk", type=str, location="args", required=True)
self.parser.add_argument("path", type=str, location="args", required=True)
# logger.info(request.form)
self.parser.add_argument("disk", type=str, location="form", required=True)
self.parser.add_argument("path", type=str, location="form", nullable=False, required=True)
self.parser.add_argument("file", type=FileStorage, location="files", required=True)
args = self.parser.parse_args()
try:
# logger.info(args.path)
file = request.files.get("file") # args.get('file')
result, message = signalManager.actionPostFileUpdate.emit(args.disk, args.path, file)
if result:
return { 'config': result, 'result': { 'message': None, 'status': "success" } }
return { 'config': result, 'result': { 'message': "file update success", 'status': "success" } }
return { 'information': list(message), 'result': { 'message': "no data", 'status': "fail" } }
except Exception as e:
current_app.logger.error(e)
......@@ -141,7 +145,94 @@ class FileUpload(Resource):
try:
fileList = request.files.getlist('fileList')
result, message = signalManager.actionPostFileUpload.emit()
result, message = signalManager.actionPostFileUpload.emit(args.disk, args.path, args.overwrite, fileList)
if result:
return { 'config': result, 'result': { 'message': None, 'status': "success" } }
return { 'information': list(message), 'result': { 'message': "no data", 'status': "fail" } }
except Exception as e:
current_app.logger.error(e)
return response_result(ResponseCode.HTTP_SERVER_ERROR)
class FileDelete(Resource):
def __init__(self):
# 特殊参数,即不是从json获取参数的接口,可以将这个注释打开
self.parser = RequestParser()
def post(self):
# 特殊参数,即不是从json获取参数的接口,可以将这个注释打开
self.parser.add_argument("disk", type=str, location="json", required=True)
# self.parser.add_argument("items", type=fields.Raw, location="json", required=True) # items: [{path: "code/app.php", type: "file"}]
args = self.parser.parse_args()
try:
json_payload = request.json
logger.info(json_payload)
if not json_payload:
return False, ResponseCode.HTTP_INVAILD_REQUEST
result, message = signalManager.actionPostFileDelete.emit(**json_payload)
if result:
return { 'config': result, 'result': { 'message': None, 'status': "success" } }
return { 'information': list(message), 'result': { 'message': "no data", 'status': "fail" } }
except Exception as e:
traceback.print_exc()
current_app.logger.error(e)
return response_result(ResponseCode.HTTP_SERVER_ERROR)
class FileCreate(Resource):
def __init__(self):
# 特殊参数,即不是从json获取参数的接口,可以将这个注释打开
self.parser = RequestParser()
def post(self):
# 特殊参数,即不是从json获取参数的接口,可以将这个注释打开
self.parser.add_argument("disk", type=str, location="json", required=True)
self.parser.add_argument("path", type=str, location="json", required=True)
self.parser.add_argument("name", type=str, location="json", required=True)
args = self.parser.parse_args()
try:
result, message = signalManager.actionPostFileCreate.emit(args.disk, args.path, args.name)
if result:
return { 'config': result, 'result': { 'message': None, 'status': "success" } }
return { 'information': list(message), 'result': { 'message': "no data", 'status': "fail" } }
except Exception as e:
current_app.logger.error(e)
return response_result(ResponseCode.HTTP_SERVER_ERROR)
class FileCreateDir(Resource):
def __init__(self):
# 特殊参数,即不是从json获取参数的接口,可以将这个注释打开
self.parser = RequestParser()
def post(self):
# 特殊参数,即不是从json获取参数的接口,可以将这个注释打开
self.parser.add_argument("disk", type=str, location="json", required=True)
self.parser.add_argument("path", type=str, location="json", required=True)
self.parser.add_argument("name", type=str, location="json", required=True)
args = self.parser.parse_args()
try:
result, message = signalManager.actionPostFileCreateDir.emit(args.disk, args.path, args.name)
if result:
return { 'config': result, 'result': { 'message': None, 'status': "success" } }
return { 'information': list(message), 'result': { 'message': "no data", 'status': "fail" } }
except Exception as e:
current_app.logger.error(e)
return response_result(ResponseCode.HTTP_SERVER_ERROR)
class FilePaste(Resource):
def __init__(self):
# 特殊参数,即不是从json获取参数的接口,可以将这个注释打开
self.parser = RequestParser()
def post(self):
# 特殊参数,即不是从json获取参数的接口,可以将这个注释打开
self.parser.add_argument("disk", type=str, location="json", required=True)
self.parser.add_argument("clipboard", type=dict, location="json", required=True)
args = self.parser.parse_args()
try:
result, message = signalManager.actionPostFilePaste.emit(args.disk, args.items)
if result:
return { 'config': result, 'result': { 'message': None, 'status': "success" } }
return { 'information': list(message), 'result': { 'message': "no data", 'status': "fail" } }
......@@ -161,7 +252,6 @@ class FileDownload(Resource):
args = self.parser.parse_args()
try:
logger.info(args.path)
result, message = signalManager.actionGetFileDown.emit(args.disk, args.path)
if result:
resp, mime = result
......
'''
Author: your name
Date: 2021-07-15 09:33:39
LastEditTime: 2021-07-17 20:46:51
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath: \evm-store\tools\build_out\webcreator\response.py
'''
# -*- coding: utf_8 -*-
'''
......@@ -79,6 +87,16 @@ class ResponseCode(object):
USER_EXISTS = (1010002, 'user already exists')
USER_PASSWORD_ERROR = (1010003, 'password error')
# 应用模块
APPLICATION_NOT_EXISTS = (3010001, 'application not exists')
APPLICATION_EXISTS = (3010002, 'application already exists')
# 文件管理模块
FILE_NOT_EXISTS = (3010001, 'file not exists')
FILE_EXISTS = (3010002, 'file already exists')
DIRECTORY_NOT_EXISTS = (3010003, 'file not exists')
DIRECTORY_EXISTS = (3010004, 'file already exists')
def response_result(response, msg=None, data=None, **kwargs):
c, m = response
......
......@@ -83,7 +83,8 @@ export default {
* @param file
*/
updateFile(state, file) {
const itemIndex = state.files.findIndex((el) => el.basename === file.basename);
const itemIndex = state.files.findIndex((el) => file && el.basename === file.basename);
if (itemIndex !== -1) state.files[itemIndex] = file;
},
......
<template>
<div class="modal-content fm-modal-text-edit">
<div class="modal-header">
<h5 class="modal-title w-75 text-truncate">
{{ lang.modal.editor.title }} <small class="text-muted pl-3">{{ selectedItem.basename }}</small>
</h5>
<button type="button" class="close" aria-label="Close" v-on:click="hideModal">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<codemirror ref="fmCodeEditor" v-model="code" :options="cmOptions"/>
</div>
<div class="modal-footer">
<button class="btn btn-info"
v-on:click="updateFile">{{ lang.btn.submit }}
</button>
<button class="btn btn-light" v-on:click="hideModal">{{ lang.btn.cancel }}</button>
</div>
<div class="modal-content fm-modal-text-edit">
<div class="modal-header">
<h5 class="modal-title w-75 text-truncate">
{{ lang.modal.editor.title }}
<small class="text-muted pl-3">{{ selectedItem.basename }}</small>
</h5>
<button
type="button"
class="close"
aria-label="Close"
v-on:click="hideModal"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<codemirror ref="fmCodeEditor" v-model="code" :options="cmOptions" />
</div>
<div class="modal-footer">
<button class="btn btn-info" v-on:click="updateFile">
{{ lang.btn.submit }}
</button>
<button class="btn btn-light" v-on:click="hideModal">
{{ lang.btn.cancel }}
</button>
</div>
</div>
</template>
<script>
import { codemirror } from 'vue-codemirror';
import 'codemirror/mode/shell/shell';
import 'codemirror/mode/css/css';
import 'codemirror/mode/sass/sass';
import 'codemirror/mode/htmlmixed/htmlmixed';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/vue/vue';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/mode/xml/xml';
import 'codemirror/mode/clike/clike';
import 'codemirror/mode/php/php';
import 'codemirror/mode/sql/sql';
import 'codemirror/mode/lua/lua';
import 'codemirror/mode/perl/perl';
import 'codemirror/mode/python/python';
import 'codemirror/mode/swift/swift';
import 'codemirror/mode/ruby/ruby';
import 'codemirror/mode/go/go';
import 'codemirror/mode/yaml/yaml';
import 'codemirror/mode/properties/properties';
import modal from '../mixins/modal';
import translate from '@/utils/translate';
import { codemirror } from "vue-codemirror";
import "codemirror/mode/shell/shell";
import "codemirror/mode/css/css";
import "codemirror/mode/sass/sass";
import "codemirror/mode/htmlmixed/htmlmixed";
import "codemirror/mode/javascript/javascript";
import "codemirror/mode/vue/vue";
import "codemirror/mode/markdown/markdown";
import "codemirror/mode/xml/xml";
import "codemirror/mode/clike/clike";
import "codemirror/mode/php/php";
import "codemirror/mode/sql/sql";
import "codemirror/mode/lua/lua";
import "codemirror/mode/perl/perl";
import "codemirror/mode/python/python";
import "codemirror/mode/swift/swift";
import "codemirror/mode/ruby/ruby";
import "codemirror/mode/go/go";
import "codemirror/mode/yaml/yaml";
import "codemirror/mode/properties/properties";
import modal from "../mixins/modal";
import translate from "@/utils/translate";
import EventBus from "@/utils/eventBus";
export default {
name: 'TextEdit',
name: "TextEdit",
mixins: [modal, translate],
components: { codemirror },
data() {
return {
code: '',
code: "",
};
},
mounted() {
// get file for edit
this.$store.dispatch('fm/getFile', {
disk: this.selectedDisk,
path: this.selectedItem.path,
}).then((response) => {
// add code
this.code = response.data;
this.$store
.dispatch("fm/getFile", {
disk: this.selectedDisk,
path: this.selectedItem.path,
})
.then((response) => {
// add code
this.code = response.data;
// set size
this.$refs.fmCodeEditor.codemirror.setSize(null, this.editorHeight);
});
// set size
this.$refs.fmCodeEditor.codemirror.setSize(null, this.editorHeight);
});
},
computed: {
/**
......@@ -72,7 +83,7 @@ export default {
* @returns {*}
*/
selectedDisk() {
return this.$store.getters['fm/selectedDisk'];
return this.$store.getters["fm/selectedDisk"];
},
/**
......@@ -80,7 +91,7 @@ export default {
* @returns {*}
*/
selectedItem() {
return this.$store.getters['fm/selectedItems'][0];
return this.$store.getters["fm/selectedItems"][0];
},
/**
......@@ -89,8 +100,10 @@ export default {
*/
cmOptions() {
return {
mode: this.$store.state.fm.settings.textExtensions[this.selectedItem.extension],
theme: 'blackboard',
mode: this.$store.state.fm.settings.textExtensions[
this.selectedItem.extension
],
theme: "blackboard",
lineNumbers: true,
line: true,
};
......@@ -113,17 +126,25 @@ export default {
updateFile() {
const formData = new FormData();
// add disk name
formData.append('disk', this.selectedDisk);
formData.append("disk", this.selectedDisk);
// add path
formData.append('path', this.selectedItem.dirname);
formData.append("path", this.selectedItem.dirname);
// add updated file
formData.append('file', new Blob([this.code]), this.selectedItem.basename);
formData.append(
"file",
new Blob([this.code]),
this.selectedItem.basename
);
this.$store.dispatch('fm/updateFile', formData).then((response) => {
this.$store.dispatch("fm/updateFile", formData).then((response) => {
// if file updated successfully
if (response.data.result.status === 'success') {
if (response.data.result.status === "success") {
// close modal window
this.hideModal();
EventBus.$emit("addNotification", {
status: "success",
message: response.data.result.message,
});
}
});
},
......@@ -132,12 +153,12 @@ export default {
</script>
<style lang="scss">
@import '~codemirror/lib/codemirror.css';
@import '~codemirror/theme/blackboard.css';
@import "~codemirror/lib/codemirror.css";
@import "~codemirror/theme/blackboard.css";
.fm-modal-text-edit {
.modal-body {
padding: 0;
}
}
.fm-modal-text-edit {
.modal-body {
padding: 0;
}
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment