refactor: 删除fs-tree-view组件,用antd Tree重新实现

This commit is contained in:
duanhongbo committed 2020-11-20 18:24:34 +08:00
1 parent 130aec7363
commit fcb27ff156
14 files changed
+224 -1762

No files matched your search

-71
View File
@@ -1,71 +0,0 @@
.container {
width: 250px;
height: 100vh;
overflow: hidden;
resize: inline;
}
.indent-root {
padding-left: 5px;
}
.indent {
padding-left: 25px;
}
.mb {
padding-bottom: 10px;
}
.item-wrapper {
cursor: pointer;
width: 100%;
white-space: nowrap;
}
.icon {
padding-right: 5px;
}
.icon.folder {
color: #fab019;
}
.tomato {
color: #e73934;
}
.red {
color: red;
}
.orange {
color: #dd5b26;
}
.blue {
color: #2372ba;
}
.green {
color: #08743b;
}
.purple {
color: #5b2d8d;
}
.grey {
color: #303c42;
}
.brown {
color: #574038;
}
.yellow {
color: #fab019;
}
.dragthis {
border: 1px solid blue;
width: 100px;
height: 100px;
}
.drophere {
border: 1px dashed #000;
width: 200px;
height: 200px;
}
-157
View File
@@ -1,157 +0,0 @@
import React from "react";
import icon from "../icon";
import request from "../../services/request";
import "./search.css";
interface SearchProp {
basePath: string;
onItemSelected?: Function;
}
interface SearchState {
query: string;
result: { name: string; path: string }[];
showSearchLoader: boolean;
showSearchBox: boolean;
}
class Search extends React.Component<SearchProp, SearchState> {
state: SearchState = {
query: "",
result: [],
showSearchLoader: false,
showSearchBox: false,
};
constructor(props: SearchProp) {
super(props);
}
runDebounce = this.debounce(this.doSearch, 1000);
debounce(fn: Function, delay: number) {
let timer: NodeJS.Timeout;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
};
}
async doSearch(query: string) {
if (!query) {
this.setState({
query: "",
result: [],
showSearchBox: false,
showSearchLoader: false,
});
return;
}
const dirPath = this.props.basePath;
const result = await request.search(dirPath, query);
this.setState({
query,
result,
showSearchBox: true,
showSearchLoader: false,
});
}
handleChange = (query: string) => {
if (!query) {
this.setState({
query: "",
});
return;
}
this.setState({
query,
showSearchBox: true,
showSearchLoader: true,
});
};
clearSearch = (event: React.KeyboardEvent<HTMLInputElement>) => {
const keyCode = event.which || event.keyCode;
if (keyCode === 27) {
// Clear search
this.setState({
query: "",
result: [],
showSearchBox: false,
});
}
};
showResults = () => {
return (
<div className="item-wrapper search-results">
{this.state.result.map((item) => (
<div
onClick={() => this.props.onItemSelected(item)}
key={item.path}
className="indent mb search-item"
>
<div>
{icon.file(item)} <span>{item.name}</span>
</div>
<div className="search-path">{item.path}</div>
</div>
))}
</div>
);
};
emptyResults = () => {
return (
<div className="item-wrapper search-results empty-results">
<span className="tomato">{icon.close()}</span> No Matching Results
</div>
);
};
loadingResults = () => {
return <div className="item-wrapper search-results empty-results">{icon.loading()}</div>;
};
results = () => {
if (this.state.showSearchLoader) {
return this.loadingResults();
}
if (this.state.result.length > 0) {
return this.showResults();
}
return this.emptyResults();
};
render() {
return (
<div className="search-wrapper">
<span className="search-icon">{icon.search()}</span>
<input
type="text"
className="fa search"
placeholder="Search"
onChange={(event) => {
this.handleChange(event.target.value);
this.runDebounce();
}}
onKeyDown={this.clearSearch}
value={this.state.query}
/>
<div style={{ display: this.state.showSearchBox ? "block" : "none" }}>
{this.results()}
</div>
</div>
);
}
}
export default Search;
-53
View File
@@ -1,53 +0,0 @@
/* Search Styles */
.search-wrapper {
height: 35px;
width: 100%;
padding-bottom: 10px;
position: relative;
}
.search {
width: 100%;
height: 100%;
border: 1px solid rgb(180, 179, 179);
border-radius: 0;
padding-left: 30px;
color: grey;
font-size: 0.85em;
font-weight: 500;
/* border: 1px solid purple; */
}
.search-icon {
position: absolute;
padding-left: 8px;
padding-top: 12px;
color: grey;
font-size: 0.85em;
font-weight: 200;
}
.search-results {
background: #ffffff;
padding-top: 10px;
width: 100%;
min-height: 100vh;
height: auto;
}
.empty-results {
padding-top: 20px;
text-align: center;
font-size: 0.8em;
}
.search-item:hover > div {
background: rgba(0, 0, 0, 0.15);
}
.search-path {
font-size: 10px;
color: #6e6e6e;
padding: 5px 0;
padding-left: 20px;
}
-127
View File
@@ -1,127 +0,0 @@
import { Menu, Dropdown, Input } from "antd";
import React from "react";
import icon from "../icon";
export interface Node {
name: string;
path: string;
extension?: string;
type?: "directory" | "file";
//size?: number;
isOpen?: boolean;
isEditing?: boolean;
children?: NodeDict;
}
export type NodeDict = { [key: string]: Node };
export type NodeAction = "create" | "create_folder" | "rename" | "delete" | "reveal_in_explorer";
export interface TreeNodeProp {
node: Node;
currentFile: {
name: string;
path: string;
};
contextMenu: (node: Node) => JSX.Element;
handleClick?: (event: React.MouseEvent, selectedNode: Node) => void;
draggable?: boolean;
onDragStart?: (event: React.DragEvent<HTMLDivElement>, data: Node) => void;
onDrag?: (event: React.DragEvent<HTMLDivElement>) => void;
onDragOver?: (event: React.DragEvent<HTMLDivElement>) => void;
onDrop?: (event: React.DragEvent<HTMLDivElement>, data: Node) => void;
onRenameNode?:(node:Node,newFileName:string) => void;
disableContextMenu?: boolean;
}
let EditingText = "";
export default function TreeNode(props: TreeNodeProp) {
const {
node,
currentFile,
contextMenu,
handleClick,
onDragStart,
onDrag,
onDragOver,
onDrop,
onRenameNode,
disableContextMenu,
} = props;
return (
<div className="indent">
{node.children &&
Object.keys(node.children).map((path) => {
const child = node.children[path];
return (
<div
key={path}
onClick={(event) => handleClick(event, child)}
draggable={true}
onDragStart={(event) => onDragStart(event, child)}
onDrag={onDrag}
onDragOver={onDragOver}
onDrop={(event) => onDrop(event, child)}
>
{child.isEditing ? (
<Input
defaultValue={child.name}
onChange={(e) => {
EditingText = e.currentTarget.value;
}}
onFocus={(e)=>{
EditingText=child.name;
//e.currentTarget.select();
e.currentTarget.setSelectionRange(0,EditingText.lastIndexOf("."),"forward");
}}
onPressEnter={(e) => {
if(child.isEditing){
child.isEditing = false;
onRenameNode(child,EditingText);
}
}}
onBlur={(e)=>{
console.log("onBlur ", EditingText);
if(child.isEditing){
child.isEditing = false;
onRenameNode(child,EditingText);
}
}}
autoFocus={true}
/>
) : (
<Dropdown overlay={contextMenu(child)} trigger={["contextMenu"]}>
<div
className={
currentFile && currentFile.path === child.path
? "item-wrapper blue mb"
: "item-wrapper mb"
}
>
{icon.file(child)} {child.name}
</div>
</Dropdown>
)}
<TreeNode
node={child}
currentFile={currentFile}
contextMenu={contextMenu}
handleClick={handleClick}
draggable={true}
onDragStart={onDragStart}
onDrag={onDrag}
onDragOver={onDragOver}
onDrop={onDrop}
disableContextMenu={disableContextMenu}
/>
</div>
);
})}
</div>
);
}
-539
View File
@@ -1,539 +0,0 @@
import React, { ChangeEvent, Component } from "react";
import path from "path";
import request from "../../services/request";
import TreeNode, { Node, NodeAction, NodeDict } from "./TreeNode";
import icon from "../icon";
import Search from "../Search";
import { Modal, Menu, Dropdown } from "antd";
import { shell } from "electron";
export interface TreeProps {
className: string;
basePath: string;
disableContextMenu?: boolean;
filter: (path: string) => boolean;
onItemSelected?: (node: Node) => void;
onItemDeleted?: (node: Node) => void;
}
interface TreeState {
nodes?: NodeDict;
currentFile?: { name: string; path: string };
overwrite?: boolean;
dragdrop?: {
sourceNode: Node;
destinationNode?: Node;
};
}
export default class Tree extends Component<TreeProps, TreeState> {
state: TreeState = {
dragdrop: {
sourceNode: null,
destinationNode: null,
},
};
constructor(props: TreeProps) {
super(props);
}
_getRootNode() {
return {
[this.props.basePath]: {
name: path.basename(this.props.basePath),
path: this.props.basePath,
type: "directory",
isOpen: false,
children: null,
} as Node,
};
}
componentDidMount() {
this.setState({
nodes: this._getRootNode(),
});
// Open first level folders
this.openFolders();
console.log("componentDidMount", this.state);
}
openFolders = async () => {
let nodes = this._getRootNode();
const rootNode = nodes[this.props.basePath];
const children = await this.getChildNodes(rootNode, this.props.filter);
rootNode.children = children;
rootNode.isOpen = true;
for (const path in children) {
if (children.hasOwnProperty(path)) {
const node = children[path];
if (node.type === "directory") {
const subChildren = await this.getChildNodes(node, this.props.filter);
node.children = { ...subChildren };
node.isOpen = true;
}
}
}
this.setState({
nodes: nodes,
});
};
getChildNodes = async (node: Node, filter: (path: string) => boolean) => {
// if (!node.children) {
// return null;
// }
let children = (await request.tree(node.path)) as NodeDict;
let result: NodeDict = {};
for (const path in children) {
if (children.hasOwnProperty(path)) {
const node = children[path];
if (node.type == "directory" || filter(node.path)) {
result[node.path] = node;
}
}
}
return result;
};
handleClick = async (event: React.MouseEvent, selectedNode: Node) => {
event.stopPropagation();
// If folder is clicked and the folder is already open, we will close it
if (selectedNode.isOpen === true) {
this.emptyChildren(this.state.nodes, selectedNode);
selectedNode.isOpen = false;
return;
}
if (selectedNode.type === "file") {
this.setState({
currentFile: {
name: selectedNode.name,
path: selectedNode.path,
},
});
}
selectedNode.isOpen = !selectedNode.isOpen;
if(selectedNode.type === "directory"){
// Get children of a selected node
const newChildren = await this.getChildNodes(selectedNode, this.props.filter);
// Add the new children to the selected folder
this.addToChildren(this.state.nodes, selectedNode, newChildren);
}
// Propagate the selected item to the parent component
this.props.onItemSelected(selectedNode);
};
handleOnMove = async () => {
try {
const response = await request.dragDrop({
source: this.state.dragdrop.sourceNode.path,
destination: this.state.dragdrop.destinationNode.path,
overwrite: this.state.overwrite,
});
if (response.status === 201) {
this.removeItem(this.state.nodes, this.state.dragdrop.sourceNode);
this.emptyChildren(this.state.nodes, this.state.dragdrop.destinationNode);
}
} catch (error) {
Modal.error({
title: "Error",
content: "Item already exists in the destination",
centered: true,
});
}
this.setState({
overwrite: false,
});
};
renameNode = async (selectedNode: Node,newFileName:string) => {
try {
const response = await request.renameNode({
oldPath: selectedNode.path,
newFileName: newFileName,
});
if (response.status === 200) {
// Add the Item to state
let newNode = selectedNode;
newNode.name = newFileName;
newNode.path = response.data.newFilePath;
this.addItem(this.state.nodes, newNode);
}
} catch (error) {
Modal.error({
title: "Error",
content: `A ${
selectedNode.type === "file" ? "file" : "folder"
} with that name already exists`,
centered: true,
});
}
};
handleOnDelete = async (selectedNode: Node) => {
const response = await request.deleteNode(selectedNode.path);
if (response.status === 200) {
this.props.onItemDeleted && this.props.onItemDeleted(selectedNode);
this.removeItem(this.state.nodes, selectedNode);
}else{
Modal.error({
title: "Delete File/Folder Error",
content: `${response.statusText}`,
centered: true,
});
}
};
handleContextMenu = (data: { node: Node; action: NodeAction }) => {
const { node, action } = data;
switch (action) {
case "create":{
//TODO new file
const baseDir = path.dirname(node.path) ;
const defaultNewName="NewTree.json";
const newFilePath = `${baseDir}${path.sep}${defaultNewName}`;
this.addToChildren(this.state.nodes, node, {
[newFilePath]:{
name:defaultNewName,
path:newFilePath,
type:"file",
isEditing:true,
isOpen: false,
children: null,
}
});
}
break;
case "create_folder":{
//TODO: new folder
const baseDir = path.dirname(node.path) ;
const defaultNewName="NewTree.json";
const newFilePath = `${baseDir}${path.sep}${defaultNewName}`;
this.addToChildren(this.state.nodes, node, {
[newFilePath]:{
name:defaultNewName,
path:newFilePath,
type:"directory",
isEditing:true,
isOpen: false,
children: null,
}
});
}
break;
case "reveal_in_explorer":{
shell.showItemInFolder(node.path);
}
break;
case "rename":{
node.isEditing = true;
const nodes = this.state.nodes;
this.setState({nodes:nodes});
}
break;
case "delete":{
Modal.confirm({
title: "Delete Confirmation",
content: `You are about to delete ${node.path}`,
centered: true,
okText: "Delete",
onOk: () => this.handleOnDelete(node),
});
}
break;
}
};
// Adds children to a selected folder
addToChildren = (nodes: NodeDict, selectedNode: Node, newChildren: NodeDict) => {
for (const path in nodes) {
const iterator = nodes[path];
if (iterator.type === "file") {
continue;
}
if (selectedNode.path.includes(iterator.path)) {
// If its exactly the same path append the new node in its children array
if (selectedNode.path === iterator.path) {
iterator.children = { ...newChildren, ...iterator.children };
// Prepare a new version of the current state
const newNodes = this.state.nodes;
// Set the new state
this.setState({
nodes: newNodes,
});
break;
} else {
// If its a partial match, then get inside its children and perform iteration until exact match is not found
this.addToChildren(iterator.children, selectedNode, newChildren);
}
}
}
};
// Removes children from a selected folder
emptyChildren = (nodes: NodeDict, selectedNode: Node) => {
for (let path in nodes) {
const iterator = nodes[path];
if (iterator.type === "file") {
continue;
}
if (selectedNode.path.includes(iterator.path)) {
// If its exactly the same path append the new node in its children array
if (selectedNode.path === iterator.path) {
iterator.children = {};
iterator.isOpen = false;
// Prepare a new version of the current state
const newNodes = this.state.nodes;
// Set the new state
this.setState({
nodes: newNodes,
});
break;
} else {
// If its a partial match, then get inside its children and perform iteration until exact match is not found
this.emptyChildren(iterator.children, selectedNode);
}
}
}
};
// Add an item (file or folder) to the nodes array
addItem = (nodes: NodeDict, selectedNode: Node) => {
for (const path in nodes) {
const iterator = nodes[path];
if (selectedNode.path.includes(iterator.path)) {
// If its exactly the same path append the new node in its children object
if (selectedNode.path === iterator.path) {
// Add the element to the nodes object
nodes[path] = selectedNode;
// Prepare a new version of the current state
const newNodes = this.state.nodes;
// Set the new state
this.setState({
nodes: newNodes,
});
break;
} else {
// If its a partial match, then get inside its children and perform iteration until exact match is not found
this.addItem(iterator.children, selectedNode);
}
}
}
};
// Removes an item (file or folder) from the nodes array
removeItem = (nodes: NodeDict, selectedNode: Node) => {
for (const path in nodes) {
const iterator = nodes[path];
if (selectedNode.path.includes(iterator.path)) {
// If its exactly the same path append the new node in its children array
if (selectedNode.path === iterator.path) {
// Remove the element from the nodes array
// nodes.splice(path, 1);
delete nodes[path];
// Prepare a new version of the current state
const newNodes = this.state.nodes;
// Set the new state
this.setState({
nodes: newNodes,
});
break;
} else {
// If its a partial match, then get inside its children and perform iteration until exact match is not found
this.removeItem(iterator.children, selectedNode);
}
}
}
};
// Toggles the overwrite checkbox
toggleOverwrite = () => {
this.setState({
overwrite: !this.state.overwrite,
});
};
// Drag & Drop handlers
onDrop = (event: React.DragEvent<HTMLDivElement>, selectedNode: Node) => {
event.preventDefault();
event.stopPropagation();
if (selectedNode.type === "file") {
Modal.error({
title: "Error",
content: "Destination is not a directory",
centered: true,
});
return;
}
this.setState(
{
dragdrop: {
sourceNode: this.state.dragdrop.sourceNode,
destinationNode: selectedNode,
},
},
async () => {
Modal.confirm({
title: "Confirm Move",
content: `
<p>
You are about to move <strong>${this.state.dragdrop.sourceNode.name}</strong>
</p>
<p>to</p>
<p>
${selectedNode.path}
</p>`,
centered: true,
okText: "Delete",
onOk: () => this.handleOnMove(),
});
}
);
};
onDrag = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
event.stopPropagation();
};
onDragOver = (event: React.DragEvent<HTMLDivElement>) => event.preventDefault();
onDragStart = (event: React.DragEvent<HTMLDivElement>, selectedNode: Node) => {
event.stopPropagation();
this.setState({
dragdrop: {
sourceNode: selectedNode,
destinationNode: this.state.dragdrop.destinationNode,
},
});
const id = (event.target as HTMLDivElement).id;
event.dataTransfer.setData("text/plain", id);
};
renderContextMeun(node: Node) {
return (
<Menu>
<Menu.Item
key="create"
onClick={(info) => {
this.handleContextMenu({ node: node, action: "create" });
}}
>
New Tree
</Menu.Item>
<Menu.Item
key="create_folder"
onClick={(info) => {
this.handleContextMenu({ node: node, action: "create_folder" });
}}
>
New Folder
</Menu.Item>
<Menu.Item
key="reveal_in_explorer"
onClick={(info) => {
this.handleContextMenu({ node: node, action: "reveal_in_explorer" });
}}
>
Reveal In File Explorer
</Menu.Item>
<Menu.Divider/>
<Menu.Item
key="rename"
onClick={(info) => {
this.handleContextMenu({ node: node, action: "rename" });
}}
>
Rename
</Menu.Item>
<Menu.Item
key="delete"
onClick={(info) => {
this.handleContextMenu({ node: node, action: "delete" });
}}
>
Delete
</Menu.Item>
</Menu>
);
}
render() {
if (!this.state.nodes) {
return <p>Loading...</p>;
}
return (
<div className={`container ${this.props.className}`}>
<Search basePath={this.props.basePath} onItemSelected={this.props.onItemSelected} />
<div className="indent-root">
{Object.keys(this.state.nodes).map((path) => {
const node = this.state.nodes[path];
return (
<div key={path}>
<Dropdown
overlay={this.renderContextMeun(node)}
trigger={["contextMenu"]}
>
<div
onClick={(event) => {
this.handleClick(event, node);
}}
className="item-wrapper mb"
>
{icon.file(node)}{" "}{node.name}
</div>
</Dropdown>
<TreeNode
node={node}
currentFile={this.state.currentFile}
contextMenu={this.renderContextMeun.bind(this)}
handleClick={this.handleClick}
onDragStart={this.onDragStart}
onDrag={this.onDrag}
onDragOver={this.onDragOver}
onDrop={this.onDrop}
onRenameNode={this.renameNode}
disableContextMenu={this.props.disableContextMenu}
/>
</div>
);
})}
</div>
</div>
);
}
}
-34
View File
@@ -1,34 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import { FolderOutlined,FolderOpenOutlined,FileTextOutlined,SearchOutlined,CloseOutlined,LoadingOutlined} from '@ant-design/icons';
import { Node,NodeDict } from "./Tree/TreeNode";
const FileIcon = (node:Node) => {
if (node.type === "file") {
return (
<FileTextOutlined />
);
}
return node.isOpen ?(<FolderOpenOutlined />):(<FolderOutlined />)
};
const SearchIcon = () => {
return <SearchOutlined />;
};
const CloseIcon = () => {
return <CloseOutlined />;
};
const LoadingIcon = ()=>{
return <LoadingOutlined />
}
export default {
file: FileIcon,
search: SearchIcon,
close: CloseIcon,
loading: LoadingIcon
};
-3
View File
@@ -1,3 +0,0 @@
import "./App.css";
import Tree from "./components/Tree/index";
export default Tree;
-157
View File
@@ -1,157 +0,0 @@
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
const fs = require("fs");
const fsx = require("fs-extra");
const path = require("path");
const rimraf = require("rimraf");
const glob = require("glob");
const sanitize = require("sanitize-filename");
function decodeUrlParam(s) {
return decodeURIComponent(s);
}
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Tree
app.get("/", (req, res) => {
const dirPath = decodeUrlParam(req.query.path) || "";
fs.readdir(dirPath, (err, files) => {
if (!files) {
console.log("No Files", dirPath);
return res.json({});
}
let finalList = {};
files.forEach((file) => {
try {
const fullPath = path.join(dirPath, file);
const isDirectory = fs.lstatSync(fullPath).isDirectory();
let fileProperties = {
[fullPath]: {
name: file,
path: fullPath,
size: fs.statSync(fullPath).size,
extension: path.extname(file),
type: isDirectory ? "directory" : "file",
isOpen: false,
},
};
if (isDirectory) {
delete fileProperties[fullPath].extension;
fileProperties[fullPath].children = {};
// Directories should appear on top
finalList = { ...fileProperties, ...finalList };
} else {
// Files should appear at the end
finalList = { ...finalList, ...fileProperties };
}
} catch (error) {
console.log("Error:", error);
return res.json({});
}
});
return res.json(finalList);
});
});
// Search
app.get("/search", (req, res) => {
const dirPath = decodeUrlParam(req.query.path) || "";
let query = decodeUrlParam(req.query.query) || "";
query = sanitize(query);
const maxResults = 20;
glob(`${dirPath}/**/*.*`, {}, function (er, files) {
const final = [];
let counter = 0;
for (const file of files) {
// Dont show more than maxResults
if (counter >= maxResults) {
break;
}
let pattern;
try {
pattern = new RegExp(query, "gi");
} catch (error) {
query = query.replace(/[|&;$%@"<>()+,*]/g, "");
pattern = new RegExp(query, "gi");
}
if (pattern.test(path.basename(file))) {
final.push({
name: path.basename(file),
path: file,
type: "file",
extension: path.extname(file),
});
counter++;
}
}
return res.json(final);
});
});
// For Drag and Drop
app.post("/dragdrop", (req, res) => {
const { source, destination, overwrite } = req.body;
const filename = path.basename(source);
const destinationPath = `${destination}${path.sep}${filename}`;
fsx.move(source, destinationPath, { overwrite }, (error) => {
if (error) {
return res.status(400).send("NOT_OK");
}
return res.status(201).send("OK");
});
});
// Rename a file/folder
app.put("/rename", (req, res) => {
const oldPath = decodeUrlParam(req.body.oldPath);
const newFileName = decodeUrlParam(req.body.newFileName);
const newFilePath = `${path.dirname(oldPath)}${path.sep}${sanitize(newFileName)}`;
if (oldPath == newFilePath) {
return res.json({
newFilePath,
});
} else {
fsx.move(oldPath, newFilePath, { overwrite: false }, (error) => {
if (error) {
console.log("Error", error);
return res.status(400).send("NOT_OK");
}
return res.json({
newFilePath,
});
});
}
});
// Delete file/folder
app.delete("/", (req, res) => {
rimraf(decodeUrlParam(req.query.fullPath), (error) => {
if (error) {
return res.status(400).send(error);
} else {
return res.status(200).send("OK");
}
});
});
app.listen(5000, () => {
console.log("Server running on port 5000");
});
-53
View File
@@ -1,53 +0,0 @@
import * as axios from "axios";
function encodeUrlParam(s: string) {
return encodeURIComponent(s);
}
const BASE_URL = process.env.fsTreeViewUrl || "http://localhost:5000";
const tree = async (dirPath: string) => {
const response = await axios.default.get(`${BASE_URL}/?path=${encodeUrlParam(dirPath)}`);
return response.data;
};
const search = async (dirPath: string, query: string) => {
const response = await axios.default.get(
`${BASE_URL}/search?query=${encodeUrlParam(query)}&path=${encodeUrlParam(dirPath)}`
);
return response.data;
};
const dragDrop = async (args: { source: string; destination: string; overwrite: boolean }) => {
const response = await axios.default.post(`${BASE_URL}/dragdrop`, {
source : encodeUrlParam(args.source),
destination : encodeUrlParam(args.destination),
overwrite:args.overwrite
});
return response;
};
const renameNode = async (args: { oldPath: string; newFileName: string }) => {
const response = await axios.default.put(`${BASE_URL}/rename`, {
oldPath : encodeUrlParam(args.oldPath),
newFileName : encodeUrlParam(args.newFileName),
});
return response;
};
const deleteNode = async (fullPath : string) => {
const encodeFullPath = encodeUrlParam(fullPath);
const response = await axios.default.delete(`${BASE_URL}`, {
params: { fullPath:encodeFullPath },
});
return response;
};
export default {
tree,
search,
dragDrop,
renameNode,
deleteNode,
};
-3
View File
@@ -5,9 +5,6 @@ import MainEventType from "../common/MainEventType";
import electronLocalshortcut from "electron-localshortcut";
//launch fs server listen port 5000
require("../fs-tree-view/server/server");
// 一些暴露给render-process的全局变量
export interface Global {
settings: Settings;
+19 -495
View File
@@ -365,16 +365,6 @@
"defer-to-connect": "^1.0.1"
}
},
"@types/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npm.taobao.org/@types/body-parser/download/@types/body-parser-1.19.0.tgz",
"integrity": "sha1-BoWzxH6zAG/+0RfN1VFkth+AU48=",
"dev": true,
"requires": {
"@types/connect": "*",
"@types/node": "*"
}
},
"@types/concat-stream": {
"version": "1.6.0",
"resolved": "https://registry.npm.taobao.org/@types/concat-stream/download/@types/concat-stream-1.6.0.tgz",
@@ -383,24 +373,6 @@
"@types/node": "*"
}
},
"@types/connect": {
"version": "3.4.33",
"resolved": "https://registry.npm.taobao.org/@types/connect/download/@types/connect-3.4.33.tgz?cache=0&sync_timestamp=1605052812480&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fconnect%2Fdownload%2F%40types%2Fconnect-3.4.33.tgz",
"integrity": "sha1-MWEMkB7KVzuHE8MzCrxua59YhUY=",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/cors": {
"version": "2.8.8",
"resolved": "https://registry.npm.taobao.org/@types/cors/download/@types/cors-2.8.8.tgz?cache=0&sync_timestamp=1605053910398&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fcors%2Fdownload%2F%40types%2Fcors-2.8.8.tgz",
"integrity": "sha1-MXqNhWGZXGDjW54PyqjTZmDJgJI=",
"dev": true,
"requires": {
"@types/express": "*"
}
},
"@types/d3-timer": {
"version": "1.0.10",
"resolved": "https://registry.npm.taobao.org/@types/d3-timer/download/@types/d3-timer-1.0.10.tgz?cache=0&sync_timestamp=1601417425052&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fd3-timer%2Fdownload%2F%40types%2Fd3-timer-1.0.10.tgz",
@@ -426,29 +398,6 @@
"resolved": "https://registry.npm.taobao.org/@types/estree/download/@types/estree-0.0.45.tgz",
"integrity": "sha1-6Th1cpmOXs2sIhlQ2rPow7Fq+IQ="
},
"@types/express": {
"version": "4.17.9",
"resolved": "https://registry.npm.taobao.org/@types/express/download/@types/express-4.17.9.tgz?cache=0&sync_timestamp=1605057477768&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fexpress%2Fdownload%2F%40types%2Fexpress-4.17.9.tgz",
"integrity": "sha1-9fLfat1wP/KEKK3VK97IoQkbCng=",
"dev": true,
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"@types/express-serve-static-core": {
"version": "4.17.13",
"resolved": "https://registry.npm.taobao.org/@types/express-serve-static-core/download/@types/express-serve-static-core-4.17.13.tgz",
"integrity": "sha1-2a8CXpJfyLCJvjdCO40erHgb4IQ=",
"dev": true,
"requires": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*"
}
},
"@types/form-data": {
"version": "0.0.33",
"resolved": "https://registry.npm.taobao.org/@types/form-data/download/@types/form-data-0.0.33.tgz",
@@ -490,12 +439,6 @@
"@types/unist": "*"
}
},
"@types/mime": {
"version": "2.0.3",
"resolved": "https://registry.npm.taobao.org/@types/mime/download/@types/mime-2.0.3.tgz",
"integrity": "sha1-yJO3NyHbc2mZQ7/DZTsd63+qSjo=",
"dev": true
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npm.taobao.org/@types/minimatch/download/@types/minimatch-3.0.3.tgz?cache=0&sync_timestamp=1596839141589&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fminimatch%2Fdownload%2F%40types%2Fminimatch-3.0.3.tgz",
@@ -517,12 +460,6 @@
"resolved": "https://registry.npm.taobao.org/@types/qs/download/@types/qs-6.9.5.tgz?cache=0&sync_timestamp=1605055106687&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fqs%2Fdownload%2F%40types%2Fqs-6.9.5.tgz",
"integrity": "sha1-Q0cRvdSete5p2QwdZ8NUqajssYs="
},
"@types/range-parser": {
"version": "1.2.3",
"resolved": "https://registry.npm.taobao.org/@types/range-parser/download/@types/range-parser-1.2.3.tgz",
"integrity": "sha1-fuMwunyq+5gJC+zoal7kQRWQTCw=",
"dev": true
},
"@types/react": {
"version": "16.9.51",
"resolved": "https://registry.npm.taobao.org/@types/react/download/@types/react-16.9.51.tgz",
@@ -550,16 +487,6 @@
"@types/node": "*"
}
},
"@types/serve-static": {
"version": "1.13.8",
"resolved": "https://registry.npm.taobao.org/@types/serve-static/download/@types/serve-static-1.13.8.tgz?cache=0&sync_timestamp=1605657862811&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fserve-static%2Fdownload%2F%40types%2Fserve-static-1.13.8.tgz",
"integrity": "sha1-hREp1DRDPHCCFIV0/+wmPVgwnEY=",
"dev": true,
"requires": {
"@types/mime": "*",
"@types/node": "*"
}
},
"@types/unist": {
"version": "2.0.3",
"resolved": "https://registry.npm.taobao.org/@types/unist/download/@types/unist-2.0.3.tgz",
@@ -783,15 +710,6 @@
"integrity": "sha1-wLZ4+zLWD8EhnHhNaoJv44Wut5o=",
"dev": true
},
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npm.taobao.org/accepts/download/accepts-1.3.7.tgz",
"integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"acorn": {
"version": "6.4.2",
"resolved": "https://registry.npm.taobao.org/acorn/download/acorn-6.4.2.tgz?cache=0&sync_timestamp=1601885779818&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Facorn%2Fdownload%2Facorn-6.4.2.tgz",
@@ -1094,11 +1012,6 @@
"integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
"dev": true
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npm.taobao.org/array-flatten/download/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"array-tree-filter": {
"version": "2.1.0",
"resolved": "http://registry.npm.taobao.org/array-tree-filter/download/array-tree-filter-2.1.0.tgz",
@@ -1227,7 +1140,8 @@
"at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/at-least-node/download/at-least-node-1.0.0.tgz",
"integrity": "sha1-YCzUtG6EStTv/JKoARo8RuAjjcI="
"integrity": "sha1-YCzUtG6EStTv/JKoARo8RuAjjcI=",
"dev": true
},
"atob": {
"version": "2.1.2",
@@ -1241,22 +1155,6 @@
"integrity": "sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA=",
"dev": true
},
"axios": {
"version": "0.19.0",
"resolved": "https://registry.npm.taobao.org/axios/download/axios-0.19.0.tgz?cache=0&sync_timestamp=1603468783865&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faxios%2Fdownload%2Faxios-0.19.0.tgz",
"integrity": "sha1-jgm/89kSLhM/e4EByPvdAO09Krg=",
"requires": {
"follow-redirects": "1.5.10",
"is-buffer": "^2.0.2"
},
"dependencies": {
"is-buffer": {
"version": "2.0.5",
"resolved": "https://registry.npm.taobao.org/is-buffer/download/is-buffer-2.0.5.tgz?cache=0&sync_timestamp=1604429388528&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-buffer%2Fdownload%2Fis-buffer-2.0.5.tgz",
"integrity": "sha1-68JS5ADSL/jXf6CYiIIaJKZYwZE="
}
}
},
"bail": {
"version": "1.0.5",
"resolved": "https://registry.npm.taobao.org/bail/download/bail-1.0.5.tgz",
@@ -1372,38 +1270,12 @@
"integrity": "sha1-vsoAVAj2Quvr6oCwQrTRjSrA7ms=",
"dev": true
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npm.taobao.org/body-parser/download/body-parser-1.19.0.tgz",
"integrity": "sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io=",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
},
"dependencies": {
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz",
"integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
}
}
},
"boolean": {
"version": "3.0.1",
"resolved": "https://registry.npm.taobao.org/boolean/download/boolean-3.0.1.tgz",
"integrity": "sha1-NezytKLuGRsLRJhvFOtfBSpcu08=",
"dev": true
"dev": true,
"optional": true
},
"boxen": {
"version": "4.2.0",
@@ -1867,11 +1739,6 @@
"integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
"dev": true
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npm.taobao.org/bytes/download/bytes-3.1.0.tgz",
"integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY="
},
"cacache": {
"version": "12.0.4",
"resolved": "https://registry.npm.taobao.org/cacache/download/cacache-12.0.4.tgz",
@@ -2277,29 +2144,6 @@
"integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
"dev": true
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.3.tgz",
"integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz",
"integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npm.taobao.org/cookie/download/cookie-0.4.0.tgz",
"integrity": "sha1-vrQ35wIrO21JAZ0IhmUwPr6cFLo="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npm.taobao.org/cookie-signature/download/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"copy-concurrently": {
"version": "1.0.5",
"resolved": "http://registry.npm.taobao.org/copy-concurrently/download/copy-concurrently-1.0.5.tgz",
@@ -2562,15 +2406,6 @@
"resolved": "http://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npm.taobao.org/cors/download/cors-2.8.5.tgz",
"integrity": "sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk=",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"create-ecdh": {
"version": "4.0.4",
"resolved": "https://registry.npm.taobao.org/create-ecdh/download/create-ecdh-4.0.4.tgz",
@@ -2802,6 +2637,7 @@
"version": "2.6.9",
"resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1600502894812&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz",
"integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
"dev": true,
"requires": {
"ms": "2.0.0"
}
@@ -2892,14 +2728,9 @@
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "http://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz",
"resolved": "https://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"des.js": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/des.js/download/des.js-1.0.1.tgz",
@@ -2910,11 +2741,6 @@
"minimalistic-assert": "^1.0.0"
}
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"detect-browser": {
"version": "5.2.0",
"resolved": "https://registry.npm.taobao.org/detect-browser/download/detect-browser-5.2.0.tgz?cache=0&sync_timestamp=1602161052538&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdetect-browser%2Fdownload%2Fdetect-browser-5.2.0.tgz",
@@ -3104,11 +2930,6 @@
"stream-shift": "^1.0.0"
}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"ejs": {
"version": "3.1.5",
"resolved": "https://registry.npm.taobao.org/ejs/download/ejs-3.1.5.tgz",
@@ -3681,7 +3502,9 @@
"encodeurl": {
"version": "1.0.2",
"resolved": "http://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"dev": true,
"optional": true
},
"end-of-stream": {
"version": "1.4.4",
@@ -3763,11 +3586,6 @@
"integrity": "sha1-Gy3HcANnbEV+x2Cy3GjttkgYhnU=",
"dev": true
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz",
@@ -3813,11 +3631,6 @@
"integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=",
"dev": true
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"events": {
"version": "3.2.0",
"resolved": "https://registry.npm.taobao.org/events/download/events-3.2.0.tgz?cache=0&sync_timestamp=1595422595227&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fevents%2Fdownload%2Fevents-3.2.0.tgz",
@@ -3878,43 +3691,6 @@
"homedir-polyfill": "^1.0.1"
}
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npm.taobao.org/express/download/express-4.17.1.tgz",
"integrity": "sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ=",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"extend": {
"version": "3.0.2",
"resolved": "http://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz",
@@ -4217,20 +3993,6 @@
}
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npm.taobao.org/finalhandler/download/finalhandler-1.1.2.tgz",
"integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"find-cache-dir": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/find-cache-dir/download/find-cache-dir-2.1.0.tgz",
@@ -4311,24 +4073,6 @@
"readable-stream": "^2.3.6"
}
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.5.10.tgz",
"integrity": "sha1-e3qfmuov3/NnhqlP9kPtB/T/Xio=",
"requires": {
"debug": "=3.1.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz?cache=0&sync_timestamp=1600502826356&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-3.1.0.tgz",
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
"requires": {
"ms": "2.0.0"
}
}
}
},
"for-in": {
"version": "1.0.2",
"resolved": "http://registry.npm.taobao.org/for-in/download/for-in-1.0.2.tgz",
@@ -4345,11 +4089,6 @@
"mime-types": "^2.1.12"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npm.taobao.org/forwarded/download/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fragment-cache": {
"version": "0.2.1",
"resolved": "http://registry.npm.taobao.org/fragment-cache/download/fragment-cache-0.2.1.tgz",
@@ -4359,11 +4098,6 @@
"map-cache": "^0.2.2"
}
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"from2": {
"version": "2.3.0",
"resolved": "http://registry.npm.taobao.org/from2/download/from2-2.3.0.tgz",
@@ -4374,40 +4108,6 @@
"readable-stream": "^2.0.0"
}
},
"fs-extra": {
"version": "9.0.1",
"resolved": "https://registry.npm.taobao.org/fs-extra/download/fs-extra-9.0.1.tgz",
"integrity": "sha1-kQ2gBiQ3ukw5/t2GPxZ1zP78ufw=",
"requires": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^1.0.0"
},
"dependencies": {
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npm.taobao.org/jsonfile/download/jsonfile-6.1.0.tgz",
"integrity": "sha1-vFWyY0eTxnnsZAMJTrE2mKbsCq4=",
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
},
"dependencies": {
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/universalify/download/universalify-2.0.0.tgz",
"integrity": "sha1-daSYTv7cSwiXXFrrc/Uw0C3yVxc="
}
}
},
"universalify": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/universalify/download/universalify-1.0.0.tgz",
"integrity": "sha1-thodoXPoQ1sv48Z9Kbmt+FlL0W0="
}
}
},
"fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/fs-minipass/download/fs-minipass-2.1.0.tgz",
@@ -4673,7 +4373,8 @@
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz",
"integrity": "sha1-Ila94U02MpWMRl68ltxGfKB6Kfs="
"integrity": "sha1-Ila94U02MpWMRl68ltxGfKB6Kfs=",
"dev": true
},
"graphlib": {
"version": "2.1.8",
@@ -4840,25 +4541,6 @@
"integrity": "sha1-SekcXL82yblLz81xwj1SSex045A=",
"dev": true
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npm.taobao.org/http-errors/download/http-errors-1.7.2.tgz",
"integrity": "sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8=",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
},
"dependencies": {
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
}
}
},
"http-response-object": {
"version": "3.0.2",
"resolved": "https://registry.npm.taobao.org/http-response-object/download/http-response-object-3.0.2.tgz",
@@ -4987,11 +4669,6 @@
"integrity": "sha1-Zlq4vE2iendKQFhOgS4+D6RbGh4=",
"dev": true
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npm.taobao.org/ipaddr.js/download/ipaddr.js-1.9.1.tgz",
"integrity": "sha1-v/OFQ+64mEglB5/zoqjmy9RngbM="
},
"is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "http://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-0.1.6.tgz",
@@ -5428,7 +5105,7 @@
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.20.tgz?cache=0&sync_timestamp=1597335994883&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.20.tgz",
"resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.20.tgz",
"integrity": "sha1-tEqbYpe8tpjxxRo1RaKzs2jVnFI="
},
"lodash.camelcase": {
@@ -5544,11 +5221,6 @@
"resolved": "https://registry.npm.taobao.org/mdast-util-to-string/download/mdast-util-to-string-1.1.0.tgz",
"integrity": "sha1-JwVVABA/UWN70H0B2gHrGWekNSc="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"memory-fs": {
"version": "0.4.1",
"resolved": "https://registry.npm.taobao.org/memory-fs/download/memory-fs-0.4.1.tgz",
@@ -5559,22 +5231,12 @@
"readable-stream": "^2.0.1"
}
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/merge-descriptors/download/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"merge2": {
"version": "1.4.1",
"resolved": "https://registry.npm.taobao.org/merge2/download/merge2-1.4.1.tgz",
"integrity": "sha1-Q2iJL4hekHRVpv19xVwMnUBJkK4=",
"dev": true
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"micromark": {
"version": "2.10.1",
"resolved": "https://registry.npm.taobao.org/micromark/download/micromark-2.10.1.tgz",
@@ -5875,7 +5537,8 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"nan": {
"version": "2.14.1",
@@ -5903,11 +5566,6 @@
"to-regex": "^3.0.1"
}
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npm.taobao.org/negotiator/download/negotiator-0.6.2.tgz",
"integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs="
},
"neo-async": {
"version": "2.6.2",
"resolved": "https://registry.npm.taobao.org/neo-async/download/neo-async-2.6.2.tgz",
@@ -6069,14 +5727,6 @@
"resolved": "https://registry.npm.taobao.org/omit.js/download/omit.js-2.0.2.tgz",
"integrity": "sha1-3ZuENvq5R6Xz/yFMslOGMeMT7C8="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"once": {
"version": "1.4.0",
"resolved": "http://registry.npm.taobao.org/once/download/once-1.4.0.tgz",
@@ -6222,11 +5872,6 @@
"integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
"dev": true
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz",
"integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ="
},
"pascalcase": {
"version": "0.1.1",
"resolved": "https://registry.npm.taobao.org/pascalcase/download/pascalcase-0.1.1.tgz",
@@ -6269,11 +5914,6 @@
"integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=",
"dev": true
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"path-type": {
"version": "2.0.0",
"resolved": "http://registry.npm.taobao.org/path-type/download/path-type-2.0.0.tgz",
@@ -6494,15 +6134,6 @@
"dev": true,
"optional": true
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npm.taobao.org/proxy-addr/download/proxy-addr-2.0.6.tgz",
"integrity": "sha1-/cIzZQVEfT8vLGOO0nLK9hS7sr8=",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"prr": {
"version": "1.0.1",
"resolved": "http://registry.npm.taobao.org/prr/download/prr-1.0.1.tgz",
@@ -6580,9 +6211,9 @@
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz",
"integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw="
"version": "6.9.4",
"resolved": "https://registry.npm.taobao.org/qs/download/qs-6.9.4.tgz",
"integrity": "sha1-kJCykNH5FyjTwi5UhDykSupatoc="
},
"querystring": {
"version": "0.2.0",
@@ -6628,32 +6259,6 @@
"safe-buffer": "^5.1.0"
}
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npm.taobao.org/range-parser/download/range-parser-1.2.1.tgz",
"integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npm.taobao.org/raw-body/download/raw-body-2.4.0.tgz",
"integrity": "sha1-oc5vucm8NWylLoklarWQWeE9AzI=",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"dependencies": {
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz",
"integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
}
}
},
"rc": {
"version": "1.2.8",
"resolved": "https://registry.npm.taobao.org/rc/download/rc-1.2.8.tgz",
@@ -7470,7 +7075,8 @@
"safer-buffer": {
"version": "2.1.2",
"resolved": "http://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz",
"integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo="
"integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=",
"dev": true
},
"sanitize-filename": {
"version": "1.6.3",
@@ -7544,38 +7150,6 @@
}
}
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npm.taobao.org/send/download/send-0.17.1.tgz",
"integrity": "sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg=",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz",
"integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE="
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz",
"integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo="
}
}
},
"serialize-error": {
"version": "7.0.1",
"resolved": "https://registry.npm.taobao.org/serialize-error/download/serialize-error-7.0.1.tgz",
@@ -7595,17 +7169,6 @@
"randombytes": "^2.1.0"
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npm.taobao.org/serve-static/download/serve-static-1.14.1.tgz",
"integrity": "sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk=",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"set-blocking": {
"version": "2.0.0",
"resolved": "http://registry.npm.taobao.org/set-blocking/download/set-blocking-2.0.0.tgz",
@@ -7641,11 +7204,6 @@
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
"dev": true
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.1.tgz",
"integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM="
},
"sha.js": {
"version": "2.4.11",
"resolved": "http://registry.npm.taobao.org/sha.js/download/sha.js-2.4.11.tgz",
@@ -7981,11 +7539,6 @@
}
}
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npm.taobao.org/statuses/download/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"stream-browserify": {
"version": "2.0.2",
"resolved": "https://registry.npm.taobao.org/stream-browserify/download/stream-browserify-2.0.2.tgz",
@@ -8396,11 +7949,6 @@
"resolved": "http://registry.npm.taobao.org/toggle-selection/download/toggle-selection-1.0.6.tgz",
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/toidentifier/download/toidentifier-1.0.0.tgz",
"integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM="
},
"trim-repeated": {
"version": "1.0.0",
"resolved": "http://registry.npm.taobao.org/trim-repeated/download/trim-repeated-1.0.0.tgz",
@@ -8512,15 +8060,6 @@
"dev": true,
"optional": true
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npm.taobao.org/type-is/download/type-is-1.6.18.tgz",
"integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"typedarray": {
"version": "0.0.6",
"resolved": "http://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz",
@@ -8651,11 +8190,6 @@
"integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=",
"dev": true
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"unset-value": {
"version": "1.0.0",
"resolved": "http://registry.npm.taobao.org/unset-value/download/unset-value-1.0.0.tgz",
@@ -8850,11 +8384,6 @@
"resolved": "http://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"v8-compile-cache": {
"version": "2.1.1",
"resolved": "https://registry.npm.taobao.org/v8-compile-cache/download/v8-compile-cache-2.1.1.tgz",
@@ -8871,11 +8400,6 @@
"spdx-expression-parse": "^3.0.0"
}
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"vfile": {
"version": "4.2.0",
"resolved": "https://registry.npm.taobao.org/vfile/download/vfile-4.2.0.tgz?cache=0&sync_timestamp=1596111366966&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvfile%2Fdownload%2Fvfile-4.2.0.tgz",
+1 -7
View File
@@ -26,9 +26,7 @@
},
"homepage": "https://github.com/zhandouxiaojiji/behavior3editor#readme",
"devDependencies": {
"@types/cors": "^2.8.8",
"@types/electron-localshortcut": "^3.1.0",
"@types/express": "^4.17.9",
"@types/node": "^14.14.8",
"@types/rimraf": "^3.0.0",
"copy-webpack-plugin": "^6.3.1",
@@ -51,11 +49,7 @@
"@types/react": "^16.9.51",
"@types/react-dom": "^16.9.8",
"antd": "^4.6.6",
"axios": "^0.19.0",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"fs-extra": "^9.0.1",
"lodash": "^4.17.20",
"react": "^16.13.1",
"react-confirm-alert": "^2.6.2",
"react-dom": "^16.13.1",
+191 -54
View File
@@ -1,17 +1,14 @@
import React, { Component } from "react";
import { Menu, Input } from "antd";
import { Menu, Input, Tree ,Dropdown} from "antd";
import * as fs from "fs";
import * as path from "path";
import { ipcRenderer } from "electron";
import MainEventType from "../common/MainEventType";
import Tree from "../fs-tree-view";
import { DataNode } from "antd/lib/tree";
import { IconType } from "rc-tree/lib/interface";
const { Search } = Input;
export interface TreeFile {
name: string;
path: string;
}
const { DirectoryTree } = Tree;
export interface PropertiesProps {
workdir: string;
@@ -19,13 +16,124 @@ export interface PropertiesProps {
onDeleteTree: (path: string) => void;
}
interface PropertiesState {
treeList: TreeFile[];
class FileDataNode implements DataNode {
checkable?: boolean;
children?: FileDataNode[];
disabled?: boolean;
disableCheckbox?: boolean;
icon?: IconType;
isLeaf?: boolean;
key: React.ReactText;
title?: React.ReactNode;
selectable?: boolean;
switcherIcon?: IconType;
className?: string;
style?: React.CSSProperties;
parent: FileDataNode;
public constructor(init?: Partial<FileDataNode>) {
Object.assign(this, init);
}
get isFolder() {
return this.isLeaf !== void 0 && !this.isLeaf;
}
get name() {
return this.title as string;
}
get path() {
return this.key as string;
}
loadChilds(recursive?: boolean) {
const folder = this.key as string;
if (folder == "" || !fs.existsSync(folder)) {
return [];
}
const files = fs.readdirSync(folder);
const list: FileDataNode[] = [];
files.forEach((filename) => {
const fullPath = path.join(folder, filename);
const stat = fs.statSync(fullPath);
const isFolder = stat.isDirectory();
const name = isFolder ? filename : filename.slice(0, -5);
const node = new FileDataNode({
title: name,
key: fullPath,
isLeaf: !isFolder,
parent: this,
});
node.children = isFolder&&recursive ? (node.loadChilds(recursive) as FileDataNode[]) : [],
list.push(node);
});
return list;
}
addChild(filePath: string) {
const isDir = fs.statSync(filePath).isDirectory();
const newNode = new FileDataNode({
title: path.basename(filePath),
key: filePath,
isLeaf: !isDir,
parent: this,
children: [],
});
this.children = [newNode, ...this.children];
}
removeFromParent() {
const parent = this.parent;
if (parent) {
const index = parent.children.indexOf(this);
parent.children.splice(index, 1);
}
}
expandSelf(recursive?: boolean) {
if (this.isFolder) this.children = this.loadChilds(recursive);
}
findChild(key: string): FileDataNode {
for (const child of this.children) {
if (child.key == key) {
return child;
} else {
const ret = child.findChild(key);
if (ret) {
return ret;
}
}
}
return null;
}
// name:string;
// path: string;
// isFolder : boolean;
// isEditing?: boolean;
// children?: NodeData[];
}
interface PropertiesState {
root: FileDataNode;
}
const NodeActions: { [x: string]: string } = {
["create"]: "New Tree",
["createFolder"]: "New Folder",
["rename"]: "Rename",
["delete"]: "Delete",
["reveal_in_explorer"]: "Reveal In File Explorer",
};
export default class Properties extends Component<PropertiesProps> {
state: PropertiesState = {
treeList: [],
root: null,
};
curWorkdir: string = "";
@@ -40,6 +148,7 @@ export default class Properties extends Component<PropertiesProps> {
ipcRenderer.on(MainEventType.CREATE_TREE, (event: any, path: string) => {
console.log("on Create tree", path);
this.props.onOpenTree(path);
this.state.root.expandSelf();
this.forceUpdate();
});
@@ -47,58 +156,86 @@ export default class Properties extends Component<PropertiesProps> {
if (workdir == "") {
return;
}
this.setState({ treeList: this.getTreeList(workdir) });
const root = this.getRootNode(workdir);
root.expandSelf();
this.setState({ root: root });
}
getTreeList(workdir: string) {
if (workdir == "" || !fs.existsSync(workdir)) {
console.log("workdir not exist", workdir);
return [];
}
const files = fs.readdirSync(workdir);
const list: TreeFile[] = [];
files.forEach((filename) => {
const stat = fs.statSync(path.join(workdir, filename));
if (stat.isFile()) {
var name = filename.slice(0, -5);
list.push({
name,
path: workdir + "/" + name + ".json",
});
}
getRootNode(workdir: string) {
return new FileDataNode({
title: path.basename(workdir),
key: workdir,
isLeaf: false,
parent: null,
children: [],
});
return list;
}
// renderContextMeun(selectedNode: FileDataNode) {
// const actions = Object.keys(NodeActions);
// return (
// <Menu>
// {actions.map((action) => {
// const displayName: string = NodeActions[action];
// return (
// <Menu.Item
// key={action}
// onClick={(info) => {
// //this.handleContextMenu({ node: selectedNode, action:{action} });
// }}
// >{displayName}
// </Menu.Item>
// );
// })}
// </Menu>
// );
// }
// renderNode(node:FileDataNode){
// return (<Dropdown
// overlay={this.renderContextMeun(node)}
// trigger={["contextMenu"]}
// >
// <div>{node.title}</div>
// </Dropdown>)
// }
render() {
console.log("render Properties");
const { onOpenTree, onDeleteTree, workdir } = this.props;
//const treeList = this.getTreeList(workdir);
if (!fs.existsSync(workdir)) {
return <div></div>;
} else {
return (
<Tree
className={""}
basePath={workdir || "/"}
disableContextMenu={false}
filter={(path) => {
return path.endsWith(".json");
}}
onItemSelected={(item) => {
if (item.type == "file") {
onOpenTree(item.path);
const root = this.state.root;
const nodes = root ? [root] : [];
return nodes.length ? (
<DirectoryTree
selectable
showLine
expandAction="click"
treeData={nodes}
defaultExpandedKeys={[root.key]}
onCheck={(node) => console.log("onCheck")}
onSelect={(keys, info) => {
console.log("onSelect", keys, info);
if (info.node.isLeaf) {
onOpenTree(keys[0] as string);
}
}}
onExpand={(keys, info) => {
console.log("onExpand", info);
if (info.expanded && !info.node.expanded) {
//let newRoot = _.cloneDeep(root);
const node = root.findChild(info.node.key as string);
if (node) {
node.expandSelf();
this.setState({ root: root });
this.forceUpdate();
}
}}
onItemDeleted={(item) => {
if (item.type == "file") {
onDeleteTree(item.path);
}
}}
></Tree>
);
}
}
}}
onLoad={(keys, info) => console.log("onLoad", keys, info)}
onRightClick={(event) => console.log("onRightClick", event)}
></DirectoryTree>
) : (
`Work Dir (${workdir}) Empty`
);
}
}
+13 -9
View File
@@ -81,15 +81,19 @@ export default class Main extends Component {
return (
<Layout className="body">
<Sider className="sider" width={250}>
<Properties
workdir={workdir}
onOpenTree={(path) => {
this.tabs.openFile(path);
}}
onDeleteTree={(path) => {
this.tabs.closeFile(path);
}}
/>
{workdir !== "" ? (
<Properties
workdir={workdir}
onOpenTree={(path) => {
this.tabs.openFile(path);
}}
onDeleteTree={(path) => {
this.tabs.closeFile(path);
}}
/>
) : (
"Please Open Workspace"
)}
</Sider>
<Content className="content">
<TreeTabs