From 8efe0c8bed31905048c0c23ca8f99838aeb6cd69 Mon Sep 17 00:00:00 2001 From: zhengkunwang <31820853+zhengkunwang223@users.noreply.github.com> Date: Sun, 15 Oct 2023 22:08:15 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=87=E4=BB=B6=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8E=92=E5=BA=8F=E5=8A=9F=E8=83=BD=20(#2541?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs https://github.com/1Panel-dev/1Panel/issues/2343 Refs https://github.com/1Panel-dev/1Panel/issues/1698 Refs https://github.com/1Panel-dev/1Panel/issues/1032 --- backend/utils/cmd/cmd.go | 89 ++++++------------- backend/utils/files/file_op.go | 75 +--------------- backend/utils/files/fileinfo.go | 78 +++++++++++++--- frontend/package.json | 4 +- frontend/src/api/interface/file.ts | 2 + .../src/components/complex-table/index.vue | 6 ++ frontend/src/lang/modules/en.ts | 1 + frontend/src/lang/modules/tw.ts | 1 + frontend/src/lang/modules/zh.ts | 1 + .../src/views/host/file-management/index.vue | 43 +++++++-- 10 files changed, 147 insertions(+), 153 deletions(-) diff --git a/backend/utils/cmd/cmd.go b/backend/utils/cmd/cmd.go index 7ccaba64d..c7dfa59df 100644 --- a/backend/utils/cmd/cmd.go +++ b/backend/utils/cmd/cmd.go @@ -24,22 +24,26 @@ func Exec(cmdStr string) (string, error) { return "", buserr.New(constant.ErrCmdTimeout) } if err != nil { - errMsg := "" - if len(stderr.String()) != 0 { - errMsg = fmt.Sprintf("stderr: %s", stderr.String()) - } - if len(stdout.String()) != 0 { - if len(errMsg) != 0 { - errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String()) - } else { - errMsg = fmt.Sprintf("stdout: %s", stdout.String()) - } - } - return errMsg, err + return handleErr(stdout, stderr, err) } return stdout.String(), nil } +func handleErr(stdout, stderr bytes.Buffer, err error) (string, error) { + errMsg := "" + if len(stderr.String()) != 0 { + errMsg = fmt.Sprintf("stderr: %s", stderr.String()) + } + if len(stdout.String()) != 0 { + if len(errMsg) != 0 { + errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String()) + } else { + errMsg = fmt.Sprintf("stdout: %s", stdout.String()) + } + } + return errMsg, err +} + func ExecWithTimeOut(cmdStr string, timeout time.Duration) (string, error) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() @@ -52,18 +56,7 @@ func ExecWithTimeOut(cmdStr string, timeout time.Duration) (string, error) { return "", buserr.New(constant.ErrCmdTimeout) } if err != nil { - errMsg := "" - if len(stderr.String()) != 0 { - errMsg = fmt.Sprintf("stderr: %s", stderr.String()) - } - if len(stdout.String()) != 0 { - if len(errMsg) != 0 { - errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String()) - } else { - errMsg = fmt.Sprintf("stdout: %s", stdout.String()) - } - } - return errMsg, err + return handleErr(stdout, stderr, err) } return stdout.String(), nil } @@ -114,18 +107,7 @@ func Execf(cmdStr string, a ...interface{}) (string, error) { cmd.Stderr = &stderr err := cmd.Run() if err != nil { - errMsg := "" - if len(stderr.String()) != 0 { - errMsg = fmt.Sprintf("stderr: %s", stderr.String()) - } - if len(stdout.String()) != 0 { - if len(errMsg) != 0 { - errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String()) - } else { - errMsg = fmt.Sprintf("stdout: %s", stdout.String()) - } - } - return errMsg, err + return handleErr(stdout, stderr, err) } return stdout.String(), nil } @@ -137,18 +119,7 @@ func ExecWithCheck(name string, a ...string) (string, error) { cmd.Stderr = &stderr err := cmd.Run() if err != nil { - errMsg := "" - if len(stderr.String()) != 0 { - errMsg = fmt.Sprintf("stderr: %s", stderr.String()) - } - if len(stdout.String()) != 0 { - if len(errMsg) != 0 { - errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String()) - } else { - errMsg = fmt.Sprintf("stdout: %s", stdout.String()) - } - } - return errMsg, err + return handleErr(stdout, stderr, err) } return stdout.String(), nil } @@ -166,22 +137,20 @@ func ExecScript(scriptPath, workDir string) (string, error) { return "", buserr.New(constant.ErrCmdTimeout) } if err != nil { - errMsg := "" - if len(stderr.String()) != 0 { - errMsg = fmt.Sprintf("stderr: %s", stderr.String()) - } - if len(stdout.String()) != 0 { - if len(errMsg) != 0 { - errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String()) - } else { - errMsg = fmt.Sprintf("stdout: %s", stdout.String()) - } - } - return errMsg, err + return handleErr(stdout, stderr, err) } return stdout.String(), nil } +func ExecCmd(cmdStr string) error { + cmd := exec.Command("bash", "-c", cmdStr) + err := cmd.Run() + if err != nil { + return err + } + return nil +} + func CheckIllegal(args ...string) bool { if args == nil { return false diff --git a/backend/utils/files/file_op.go b/backend/utils/files/file_op.go index bd2aa7c79..f8c0ef98a 100644 --- a/backend/utils/files/file_op.go +++ b/backend/utils/files/file_op.go @@ -312,83 +312,14 @@ func (f FileOp) CopyDir(src, dst string) error { return err } dstDir := filepath.Join(dst, srcInfo.Name()) - if err := f.Fs.MkdirAll(dstDir, srcInfo.Mode()); err != nil { + if err = f.Fs.MkdirAll(dstDir, srcInfo.Mode()); err != nil { return err } - - dir, _ := f.Fs.Open(src) - obs, err := dir.Readdir(-1) - if err != nil { - return err - } - var errs []error - - for _, obj := range obs { - fSrc := filepath.Join(src, obj.Name()) - if obj.IsDir() { - err = f.CopyDir(fSrc, dstDir) - if err != nil { - errs = append(errs, err) - } - } else { - err = f.CopyFile(fSrc, dstDir) - if err != nil { - errs = append(errs, err) - } - } - } - - var errString string - for _, err := range errs { - errString += err.Error() + "\n" - } - - if errString != "" { - return errors.New(errString) - } - - return nil + return cmd.ExecCmd(fmt.Sprintf("cp -rf %s %s", src, dst+"/")) } func (f FileOp) CopyFile(src, dst string) error { - srcFile, err := f.Fs.Open(src) - if err != nil { - return err - } - defer srcFile.Close() - - srcInfo, err := f.Fs.Stat(src) - if err != nil { - return err - } - dstPath := path.Join(dst, srcInfo.Name()) - if src == dstPath { - return nil - } - - err = f.Fs.MkdirAll(filepath.Dir(dst), 0666) - if err != nil { - return err - } - - dstFile, err := f.Fs.OpenFile(dstPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775) - if err != nil { - return err - } - defer dstFile.Close() - - if _, err = io.Copy(dstFile, srcFile); err != nil { - return err - } - info, err := f.Fs.Stat(src) - if err != nil { - return err - } - if err = f.Fs.Chmod(dstFile.Name(), info.Mode()); err != nil { - return err - } - - return nil + return cmd.ExecCmd(fmt.Sprintf("cp -f %s %s", src, dst+"/")) } func (f FileOp) GetDirSize(path string) (float64, error) { diff --git a/backend/utils/files/fileinfo.go b/backend/utils/files/fileinfo.go index e4709bf1c..50fd7d30b 100644 --- a/backend/utils/files/fileinfo.go +++ b/backend/utils/files/fileinfo.go @@ -10,6 +10,7 @@ import ( "os/exec" "path" "path/filepath" + "sort" "strconv" "strings" "syscall" @@ -52,6 +53,8 @@ type FileOption struct { ShowHidden bool `json:"showHidden"` Page int `json:"page"` PageSize int `json:"pageSize"` + SortBy string `json:"sortBy" validate:"oneof=name size modTime"` + SortOrder string `json:"sortOrder" validate:"oneof=ascending descending"` } type FileSearchInfo struct { @@ -90,7 +93,7 @@ func NewFileInfo(op FileOption) (*FileInfo, error) { } if op.Expand { if file.IsDir { - if err := file.listChildren(op.Dir, op.ShowHidden, op.ContainSub, op.Search, op.Page, op.PageSize); err != nil { + if err := file.listChildren(op); err != nil { return nil, err } return file, nil @@ -139,7 +142,42 @@ func (f *FileInfo) search(search string, count int) (files []FileSearchInfo, tot return } -func (f *FileInfo) listChildren(dir, showHidden, containSub bool, search string, page, pageSize int) error { +func sortFileList(list []FileSearchInfo, sortBy, sortOrder string) { + switch sortBy { + case "name": + if sortOrder == "ascending" { + sort.Slice(list, func(i, j int) bool { + return list[i].Name() < list[j].Name() + }) + } else { + sort.Slice(list, func(i, j int) bool { + return list[i].Name() > list[j].Name() + }) + } + case "size": + if sortOrder == "ascending" { + sort.Slice(list, func(i, j int) bool { + return list[i].Size() < list[j].Size() + }) + } else { + sort.Slice(list, func(i, j int) bool { + return list[i].Size() > list[j].Size() + }) + } + case "modTime": + if sortOrder == "ascending" { + sort.Slice(list, func(i, j int) bool { + return list[i].ModTime().Before(list[j].ModTime()) + }) + } else { + sort.Slice(list, func(i, j int) bool { + return list[i].ModTime().After(list[j].ModTime()) + }) + } + } +} + +func (f *FileInfo) listChildren(option FileOption) error { afs := &afero.Afero{Fs: f.Fs} var ( files []FileSearchInfo @@ -147,8 +185,8 @@ func (f *FileInfo) listChildren(dir, showHidden, containSub bool, search string, total int ) - if search != "" && containSub { - files, total, err = f.search(search, page*pageSize) + if option.Search != "" && option.ContainSub { + files, total, err = f.search(option.Search, option.Page*option.PageSize) if err != nil { return err } @@ -157,34 +195,46 @@ func (f *FileInfo) listChildren(dir, showHidden, containSub bool, search string, if err != nil { return err } + var ( + dirs []FileSearchInfo + fileList []FileSearchInfo + ) for _, file := range dirFiles { - files = append(files, FileSearchInfo{ + info := FileSearchInfo{ Path: f.Path, FileInfo: file, - }) + } + if file.IsDir() { + dirs = append(dirs, info) + } else { + fileList = append(fileList, info) + } } + sortFileList(dirs, option.SortBy, option.SortOrder) + sortFileList(fileList, option.SortBy, option.SortOrder) + files = append(dirs, fileList...) } var items []*FileInfo for _, df := range files { - if dir && !df.IsDir() { + if option.Dir && !df.IsDir() { continue } name := df.Name() fPath := path.Join(df.Path, df.Name()) - if search != "" { - if containSub { + if option.Search != "" { + if option.ContainSub { fPath = df.Path name = strings.TrimPrefix(strings.TrimPrefix(fPath, f.Path), "/") } else { lowerName := strings.ToLower(name) - lowerSearch := strings.ToLower(search) + lowerSearch := strings.ToLower(option.Search) if !strings.Contains(lowerName, lowerSearch) { continue } } } - if !showHidden && IsHidden(name) { + if !option.ShowHidden && IsHidden(name) { continue } f.ItemTotal++ @@ -228,11 +278,11 @@ func (f *FileInfo) listChildren(dir, showHidden, containSub bool, search string, } items = append(items, file) } - if containSub { + if option.ContainSub { f.ItemTotal = total } - start := (page - 1) * pageSize - end := pageSize + start + start := (option.Page - 1) * option.PageSize + end := option.PageSize + start var result []*FileInfo if start < 0 || start > f.ItemTotal || end < 0 || start > end { result = items diff --git a/frontend/package.json b/frontend/package.json index df21f45c8..1c211d3bd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { - "name": "panel", + "name": "1Panel-Frontend", "private": true, - "version": "1.3", + "version": "1.7", "description": "1Panel 前端", "scripts": { "dev": "vite", diff --git a/frontend/src/api/interface/file.ts b/frontend/src/api/interface/file.ts index acc2f8f12..b26d120f5 100644 --- a/frontend/src/api/interface/file.ts +++ b/frontend/src/api/interface/file.ts @@ -30,6 +30,8 @@ export namespace File { dir?: boolean; showHidden?: boolean; containSub?: boolean; + sortBy?: string; + sortOrder?: string; } export interface SearchUploadInfo extends ReqPage { diff --git a/frontend/src/components/complex-table/index.vue b/frontend/src/components/complex-table/index.vue index 8c8c45869..9222c6b37 100644 --- a/frontend/src/components/complex-table/index.vue +++ b/frontend/src/components/complex-table/index.vue @@ -75,9 +75,15 @@ function sort(prop: string, order: string) { function clearSelects() { tableRef.value.refElTable.clearSelection(); } + +function clearSort() { + tableRef.value.refElTable.clearSort(); +} + defineExpose({ clearSelects, sort, + clearSort, }); onMounted(() => { diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 7c806e7bc..12081bf5a 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -952,6 +952,7 @@ const message = { currentSelect: 'Current Select: ', unsupportType: 'Unsupported file type', deleteHelper: 'The following resources will be deleted, this operation cannot be rolled back, continue? ', + fileHeper: 'Note: 1. Sorting is not supported after searching 2. Folders are not supported by size sorting', }, ssh: { sshAlert: diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 352be17fe..65ea1aa90 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -916,6 +916,7 @@ const message = { currentSelect: '當前選中: ', unsupportType: '不支持的文件類型', deleteHelper: '以下資源將被刪除,此操作不可回滾,是否繼續?', + fileHeper: '注意:1.搜尋之後不支援排序 2.依大小排序不支援資料夾', }, ssh: { sshAlert: '列表數據根據登錄時間排序,但請註意,切換時區或其他操作可能導致登錄日誌的時間出現偏差。', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 4be57f888..1670f1b29 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -916,6 +916,7 @@ const message = { currentSelect: '当前选中: ', unsupportType: '不支持的文件类型', deleteHelper: '以下资源将被删除,此操作不可回滚,是否继续?', + fileHeper: '注意:1.搜索之后不支持排序 2.按大小排序不支持文件夹', }, ssh: { sshAlert: '列表数据根据登录时间排序,但请注意,切换时区或其他操作可能导致登录日志的时间出现偏差。', diff --git a/frontend/src/views/host/file-management/index.vue b/frontend/src/views/host/file-management/index.vue index 04dd6dd16..9d2369a12 100644 --- a/frontend/src/views/host/file-management/index.vue +++ b/frontend/src/views/host/file-management/index.vue @@ -40,6 +40,13 @@ /> +