From 1444891d9644b72331522b8981eb5873f70f3dbe Mon Sep 17 00:00:00 2001 From: dushixiang <798148596@qq.com> Date: Wed, 3 Feb 2021 19:53:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=96=87=E4=BB=B6=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/api/routes.go | 1 - pkg/api/session.go | 76 +++---- web/src/components/access/Access.js | 21 +- web/src/components/access/FileSystem.css | 62 +++++- web/src/components/access/FileSystem.js | 262 +++++++++++++++-------- web/src/components/asset/Asset.js | 8 - web/src/utils/utils.js | 2 +- 7 files changed, 270 insertions(+), 162 deletions(-) diff --git a/pkg/api/routes.go b/pkg/api/routes.go index a4c5121..4fda0fb 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -111,7 +111,6 @@ func SetupRoutes() *echo.Echo { sessions.GET("/:id/download", SessionDownloadEndpoint) sessions.GET("/:id/ls", SessionLsEndpoint) sessions.POST("/:id/mkdir", SessionMkDirEndpoint) - sessions.DELETE("/:id/rmdir", SessionRmDirEndpoint) sessions.DELETE("/:id/rm", SessionRmEndpoint) sessions.DELETE("/:id", SessionDeleteEndpoint) sessions.GET("/:id/recording", SessionRecordingEndpoint) diff --git a/pkg/api/session.go b/pkg/api/session.go index c713466..02063cb 100644 --- a/pkg/api/session.go +++ b/pkg/api/session.go @@ -476,63 +476,47 @@ func SessionMkDirEndpoint(c echo.Context) error { return nil } -func SessionRmDirEndpoint(c echo.Context) error { - sessionId := c.Param("id") - session, err := model.FindSessionById(sessionId) - if err != nil { - return err - } - remoteDir := c.QueryParam("dir") - if "ssh" == session.Protocol { - tun, ok := global.Store.Get(sessionId) - if !ok { - return errors.New("获取sftp客户端失败") - } - fileInfos, err := tun.Subject.SftpClient.ReadDir(remoteDir) - if err != nil { - return err - } - - for i := range fileInfos { - if err := tun.Subject.SftpClient.Remove(path.Join(remoteDir, fileInfos[i].Name())); err != nil { - return err - } - } - - if err := tun.Subject.SftpClient.RemoveDirectory(remoteDir); err != nil { - return err - } - return Success(c, nil) - } else if "rdp" == session.Protocol { - drivePath, err := model.GetDrivePath() - if err != nil { - return err - } - - if err := os.RemoveAll(path.Join(drivePath, remoteDir)); err != nil { - return err - } - return Success(c, nil) - } - - return nil -} - func SessionRmEndpoint(c echo.Context) error { sessionId := c.Param("id") session, err := model.FindSessionById(sessionId) if err != nil { return err } - remoteFile := c.QueryParam("file") + key := c.QueryParam("key") if "ssh" == session.Protocol { tun, ok := global.Store.Get(sessionId) if !ok { return errors.New("获取sftp客户端失败") } - if err := tun.Subject.SftpClient.Remove(remoteFile); err != nil { + + sftpClient := tun.Subject.SftpClient + + stat, err := sftpClient.Stat(key) + if err != nil { return err } + + if stat.IsDir() { + fileInfos, err := tun.Subject.SftpClient.ReadDir(key) + if err != nil { + return err + } + + for i := range fileInfos { + if err := tun.Subject.SftpClient.Remove(path.Join(key, fileInfos[i].Name())); err != nil { + return err + } + } + + if err := tun.Subject.SftpClient.RemoveDirectory(key); err != nil { + return err + } + } else { + if err := tun.Subject.SftpClient.Remove(key); err != nil { + return err + } + } + return Success(c, nil) } else if "rdp" == session.Protocol { drivePath, err := model.GetDrivePath() @@ -540,11 +524,13 @@ func SessionRmEndpoint(c echo.Context) error { return err } - if err := os.Remove(path.Join(drivePath, remoteFile)); err != nil { + if err := os.RemoveAll(path.Join(drivePath, key)); err != nil { return err } + return Success(c, nil) } + return nil } diff --git a/web/src/components/access/Access.js b/web/src/components/access/Access.js index a03627a..85ed821 100644 --- a/web/src/components/access/Access.js +++ b/web/src/components/access/Access.js @@ -1,21 +1,15 @@ import React, {Component} from 'react'; import Guacamole from 'guacamole-common-js'; -import {Affix, Button, Col, Drawer, Dropdown, Form, Input, Menu, message, Modal, Row, Space, Tooltip} from 'antd' +import {Affix, Button, Col, Drawer, Dropdown, Form, Input, Menu, message, Modal, Row} from 'antd' import qs from "qs"; import request from "../../common/request"; import {wsServer} from "../../common/constants"; import { AppstoreTwoTone, - CloudDownloadOutlined, - CloudUploadOutlined, CopyTwoTone, - DeleteOutlined, DesktopOutlined, ExclamationCircleOutlined, - ExpandOutlined, - FolderAddOutlined, - LoadingOutlined, - ReloadOutlined + ExpandOutlined } from '@ant-design/icons'; import {exitFull, getToken, isEmpty, requestFullScreen} from "../../utils/utils"; import './Access.css' @@ -48,9 +42,6 @@ class Access extends Component { uploadHeaders: {}, keyboard: {}, protocol: '', - treeData: [], - - confirmVisible: false, confirmLoading: false, uploadVisible: false, uploadLoading: false, @@ -315,7 +306,7 @@ class Access extends Component { }; onKeyDown = (keysym) => { - if (true === this.state.clipboardVisible || true === this.state.confirmVisible) { + if (this.state.clipboardVisible || this.state.fileSystemVisible) { return true; } this.state.client.sendKeyEvent(1, keysym); @@ -598,10 +589,10 @@ class Access extends Component { { + // maskClosable={false} + onClose={() => { this.setState({ fileSystemVisible: false }); diff --git a/web/src/components/access/FileSystem.css b/web/src/components/access/FileSystem.css index 15ddcc1..ef5dc17 100644 --- a/web/src/components/access/FileSystem.css +++ b/web/src/components/access/FileSystem.css @@ -5,14 +5,64 @@ -ms-user-select: none; } -.selectedRow { - background-color: #69c0ff; +@keyframes fadeIn { + 0% { + transform: translateY(-25%); + } + 50%{ + transform: translateY(4%); + } + 65%{ + transform: translateY(-2%); + } + 80%{ + transform: translateY(2%); + } + 95%{ + transform: translateY(-1%); + } + 100% { + transform: translateY(0%); + } } -.selectedRow > .ant-table-column-sort { - background-color: #69c0ff; +.popup { + animation-name: fadeIn; + animation-duration: 0.4s; + background-clip: padding-box; + background-color: #fff; + border-radius: 4px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.75); + left: 0; + list-style-type: none; + margin: 0; + outline: none; + padding: 0; + position: fixed; + text-align: left; + top: 0; + overflow: hidden; + -webkit-box-shadow: 0 2px 8px rgba(0, 0, 0, 0.75); } -.ant-table .ant-table-tbody > tr:hover:not(.ant-table-expanded-row .selectedRow) > td { - background: #bae7ff; +.popup li { + clear: both; + /*color: rgba(0, 0, 0, 0.65);*/ + cursor: pointer; + font-size: 14px; + font-weight: normal; + line-height: 22px; + margin: 0; + padding: 5px 12px; + transition: all .3s; + white-space: nowrap; + -webkit-transition: all .3s; +} + +.popup li:hover { + background-color: #e6f7ff; +} + +.popup li > i { + margin-right: 8px; } \ No newline at end of file diff --git a/web/src/components/access/FileSystem.js b/web/src/components/access/FileSystem.js index 1a49946..1f6f6fc 100644 --- a/web/src/components/access/FileSystem.js +++ b/web/src/components/access/FileSystem.js @@ -1,21 +1,22 @@ import React, {Component} from 'react'; -import {Button, Card, Form, Input, message, Modal, Row, Space, Table, Tooltip} from "antd"; +import {Button, Card, Col, Form, Input, message, Modal, Row, Space, Table, Tooltip} from "antd"; import { CloudDownloadOutlined, CloudUploadOutlined, DeleteOutlined, - FileExcelTwoTone, - FileImageTwoTone, - FileMarkdownTwoTone, - FilePdfTwoTone, - FileTextTwoTone, - FileTwoTone, - FileWordTwoTone, - FileZipTwoTone, + ExclamationCircleOutlined, + FileExcelOutlined, + FileImageOutlined, + FileMarkdownOutlined, + FileOutlined, + FilePdfOutlined, + FileTextOutlined, + FileWordOutlined, + FileZipOutlined, FolderAddOutlined, FolderTwoTone, - LinkOutlined, ReloadOutlined, + ThunderboltTwoTone, UploadOutlined } from "@ant-design/icons"; import qs from "qs"; @@ -29,6 +30,7 @@ const formItemLayout = { labelCol: {span: 6}, wrapperCol: {span: 14}, }; +const {confirm} = Modal; class FileSystem extends Component { @@ -40,7 +42,10 @@ class FileSystem extends Component { files: [], loading: false, selectedRowKeys: [], - selectedRow: {} + selectedRow: {}, + dropdown: { + visible: false + }, } componentDidMount() { @@ -76,12 +81,6 @@ class FileSystem extends Component { }) } - mkdir = () => { - this.setState({ - confirmVisible: true - }) - } - upload = () => { this.setState({ uploadVisible: true @@ -93,28 +92,39 @@ class FileSystem extends Component { } rmdir = async () => { - if (!this.state.selectedRow.key) { - message.warning('请选择一个文件或目录'); - return; + let selectedRowKeys = this.state.selectedRowKeys; + if (selectedRowKeys === undefined || selectedRowKeys.length === 0) { + message.warning('请至少选择一个文件或目录'); } - let result; - if (this.state.selectedRow.isLeaf) { - result = await request.delete(`/sessions/${this.state.sessionId}/rm?file=${this.state.selectedRow.key}`); + + let title = ''; + if (selectedRowKeys.length === 1) { + let file = selectedRowKeys[0].substring(selectedRowKeys[0].lastIndexOf('/') + 1, selectedRowKeys[0].length); + title =

您确认要删除"{file}"吗?

; } else { - result = await request.delete(`/sessions/${this.state.sessionId}/rmdir?dir=${this.state.selectedRow.key}`); - } - if (result.code !== 1) { - message.error(result.message); - } else { - message.success('删除成功'); - let path = this.state.selectedRow.key; - let parentPath = path.substring(0, path.lastIndexOf('/')); - let items = await this.getTreeNodes(parentPath); - this.setState({ - treeData: this.updateTreeData(this.state.treeData, parentPath, items), - selectedRow: {} - }); + title = `您确认要删除所选的${selectedRowKeys.length}项目吗?`; } + confirm({ + title: title, + icon: , + content: '所选项目将立即被删除。', + onOk: async () => { + for (let i = 0; i < selectedRowKeys.length; i++) { + let rowKey = selectedRowKeys[i]; + if (rowKey === '..') { + continue; + } + let result = await request.delete(`/sessions/${this.state.sessionId}/rm?key=${rowKey}`); + if (result['code'] !== 1) { + message.error(result['message']); + } + } + await this.loadFiles(this.state.currentDirectory); + }, + onCancel() { + + }, + }); } refresh = async () => { @@ -169,6 +179,34 @@ class FileSystem extends Component { } } + getNodeTreeRightClickMenu = () => { + const {pageX, pageY, visible} = {...this.state.dropdown}; + if (visible) { + console.log(pageX, pageY) + const tmpStyle = { + left: `${pageX}px`, + top: `${pageY}px`, + }; + + let disableDownload = true; + if (this.state.selectedRowKeys.length === 1 + && !this.state.selectedRow['isDir'] + && !this.state.selectedRow['isLink']) { + disableDownload = false; + } + + return ( + + ); + } + return undefined; + }; + render() { const columns = [ @@ -182,17 +220,17 @@ class FileSystem extends Component { icon = ; } else { if (item['isLink']) { - icon = ; + icon = ; } else { const fileExtension = item['name'].split('.').pop().toLowerCase(); switch (fileExtension) { case "doc": case "docx": - icon = ; + icon = ; break; case "xls": case "xlsx": - icon = ; + icon = ; break; case "bmp": case "jpg": @@ -207,24 +245,25 @@ class FileSystem extends Component { case "psd": case "ai": case "webp": - icon = ; + icon = ; break; case "md": - icon = ; + icon = ; break; case "pdf": - icon = ; + icon = ; break; case "txt": - icon = ; + icon = ; break; case "zip": case "gz": case "tar": - icon = ; + case "tgz": + icon = ; break; default: - icon = ; + icon = ; break; } } @@ -293,53 +332,70 @@ class FileSystem extends Component { ]; const title = ( - - - 远程文件管理 -   -   - -