diff --git a/go.mod b/go.mod
index 137d4711..bb78b9a9 100644
--- a/go.mod
+++ b/go.mod
@@ -60,11 +60,11 @@ require (
require (
filippo.io/edwards25519 v1.1.0 // indirect
- github.com/G-Core/gcore-dns-sdk-go v0.3.2 // indirect
+ github.com/G-Core/gcore-dns-sdk-go v0.3.3 // indirect
github.com/boombuler/barcode v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
- github.com/gabriel-vasile/mimetype v1.4.9 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.10 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gofiber/schema v1.6.0 // indirect
diff --git a/go.sum b/go.sum
index 051f633e..1f2ded76 100644
--- a/go.sum
+++ b/go.sum
@@ -15,8 +15,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/G-Core/gcore-dns-sdk-go v0.3.2 h1:WCTKmoJ5uND69UFE318yxWuHy/h3AA28Z1OnqSYUxRk=
-github.com/G-Core/gcore-dns-sdk-go v0.3.2/go.mod h1:35t795gOfzfVanhzkFyUXEzaBuMXwETmJldPpP28MN4=
+github.com/G-Core/gcore-dns-sdk-go v0.3.3 h1:McILJSbJ5nOcT0MI0aBYhEuufCF329YbqKwFIN0RjCI=
+github.com/G-Core/gcore-dns-sdk-go v0.3.3/go.mod h1:35t795gOfzfVanhzkFyUXEzaBuMXwETmJldPpP28MN4=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -57,8 +57,8 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
-github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
-github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
+github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
+github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
@@ -106,8 +106,6 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
-github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=
github.com/google/wire v0.7.0/go.mod h1:n6YbUQD9cPKTnHXEBN2DXlOp/mVADhVErcMFb0v3J18=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@@ -218,14 +216,10 @@ github.com/libtnb/acmez/v3 v3.0.0-20250707093727-dc5aedd96413 h1:q6Qttk+i7r8JWw1
github.com/libtnb/acmez/v3 v3.0.0-20250707093727-dc5aedd96413/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/libtnb/chix v1.3.0 h1:/U+CyuxI41ooeB6M/762PHOjQlfHRg6BQjHKLZarlrM=
github.com/libtnb/chix v1.3.0/go.mod h1:o8nQLEp/UrUojBKYzw8K8sltU/h0XxI2VLZ/z7AQCQg=
-github.com/libtnb/gormstore v1.1.0 h1:VbX8u0hhyl2YFSGfkSlF/xSfFG/LE29DCLrY8MJ+uxM=
-github.com/libtnb/gormstore v1.1.0/go.mod h1:8A5QzeZxi1MpSmjUVsHTDAL6KnU84feIXMutFLPawwA=
github.com/libtnb/gormstore v1.1.1 h1:FG/3P4PuWM6/vB4weVJ31meiSaoeXns1NQlP66quKeg=
github.com/libtnb/gormstore v1.1.1/go.mod h1:8A5QzeZxi1MpSmjUVsHTDAL6KnU84feIXMutFLPawwA=
github.com/libtnb/securecookie v1.2.0 h1:2uc0PBDm0foeSTrcZ9QTX1IEjf6kFEwfgEYSIXQSKrA=
github.com/libtnb/securecookie v1.2.0/go.mod h1:ja+wNGnQzYqcqXQnJWu6icsaWi5JEBwNEMJ2ReTVDxA=
-github.com/libtnb/sessions v1.2.0 h1:g/RMcfGTC5P2BQE1IqrgppPEQ++x/QjOuiwbN9Frke8=
-github.com/libtnb/sessions v1.2.0/go.mod h1:45Bn9d6PseDINLIM1QaJrlCMbzSZ0NWpDbWkdrKJKw0=
github.com/libtnb/sessions v1.2.1 h1:O9gkEIeZuqyaxopXrUJcGxlNxmNfRBI8BOK43yLJXDI=
github.com/libtnb/sessions v1.2.1/go.mod h1:45Bn9d6PseDINLIM1QaJrlCMbzSZ0NWpDbWkdrKJKw0=
github.com/libtnb/utils v1.2.0 h1:6bTZrWn2OkNrODpCY4dhuHwbhsVRV7HICIgmZ31we98=
@@ -380,8 +374,6 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
-golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -454,8 +446,6 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
-golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
diff --git a/internal/http/request/file.go b/internal/http/request/file.go
index b6159e21..f1ac4ce1 100644
--- a/internal/http/request/file.go
+++ b/internal/http/request/file.go
@@ -7,8 +7,15 @@ import (
)
type FileList struct {
- Path string `json:"path" form:"path" validate:"required|isUnixPath"`
- Sort string `json:"sort" form:"sort"`
+ Path string `json:"path" form:"path" validate:"required|isUnixPath"`
+ Sort string `json:"sort" form:"sort"`
+ Keyword string `form:"keyword" json:"keyword"`
+ Sub bool `form:"sub" json:"sub"`
+}
+
+func (r *FileList) Prepare(req *http.Request) error {
+ r.Sub = cast.ToBool(req.FormValue("sub"))
+ return nil
}
type FilePath struct {
@@ -53,14 +60,3 @@ type FileUnCompress struct {
File string `form:"file" json:"file" validate:"required|isUnixPath"`
Path string `form:"path" json:"path" validate:"required|isUnixPath"`
}
-
-type FileSearch struct {
- Path string `form:"path" json:"path" validate:"required|isUnixPath"`
- Keyword string `form:"keyword" json:"keyword" validate:"required"`
- Sub bool `form:"sub" json:"sub"`
-}
-
-func (r *FileSearch) Prepare(req *http.Request) error {
- r.Sub = cast.ToBool(req.FormValue("sub"))
- return nil
-}
diff --git a/internal/route/http.go b/internal/route/http.go
index ddd70a15..70b704c6 100644
--- a/internal/route/http.go
+++ b/internal/route/http.go
@@ -360,7 +360,6 @@ func (route *Http) Register(r *chi.Mux) {
r.Post("/permission", route.file.Permission)
r.Post("/compress", route.file.Compress)
r.Post("/un_compress", route.file.UnCompress)
- r.Get("/search", route.file.Search)
r.Get("/list", route.file.List)
})
diff --git a/internal/service/file.go b/internal/service/file.go
index 6c78e8cb..cb57ed90 100644
--- a/internal/service/file.go
+++ b/internal/service/file.go
@@ -395,27 +395,6 @@ func (s *FileService) UnCompress(w http.ResponseWriter, r *http.Request) {
Success(w, nil)
}
-func (s *FileService) Search(w http.ResponseWriter, r *http.Request) {
- req, err := Bind[request.FileSearch](r)
- if err != nil {
- Error(w, http.StatusInternalServerError, "%v", err)
- return
- }
-
- results, err := io.SearchX(req.Path, req.Keyword, req.Sub)
- if err != nil {
- Error(w, http.StatusInternalServerError, "%v", err)
- return
- }
-
- paged, total := Paginate(r, s.formatInfo(results))
-
- Success(w, chix.M{
- "total": total,
- "items": paged,
- })
-}
-
func (s *FileService) List(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.FileList](r)
if err != nil {
@@ -423,10 +402,19 @@ func (s *FileService) List(w http.ResponseWriter, r *http.Request) {
return
}
- list, err := stdos.ReadDir(req.Path)
- if err != nil {
- Error(w, http.StatusInternalServerError, "%v", err)
- return
+ var list []stdos.DirEntry
+ if req.Keyword != "" {
+ list, err = io.SearchX(req.Path, req.Keyword, req.Sub)
+ if err != nil {
+ Error(w, http.StatusInternalServerError, "%v", err)
+ return
+ }
+ } else {
+ list, err = stdos.ReadDir(req.Path)
+ if err != nil {
+ Error(w, http.StatusInternalServerError, "%v", err)
+ return
+ }
}
switch req.Sort {
@@ -464,7 +452,10 @@ func (s *FileService) formatDir(base string, entries []stdos.DirEntry) []any {
for item := range slices.Values(entries) {
info, err := item.Info()
if err != nil {
- continue
+ continue // 直接跳过,不返回错误,不然很烦人的
+ }
+ if de, ok := item.(*io.SearchEntry); ok {
+ base = filepath.Dir(de.Path())
}
stat := info.Sys().(*syscall.Stat_t)
diff --git a/internal/service/file_windows.go b/internal/service/file_windows.go
index 19f4254b..f91c149d 100644
--- a/internal/service/file_windows.go
+++ b/internal/service/file_windows.go
@@ -53,6 +53,4 @@ func (s *FileService) Compress(w http.ResponseWriter, r *http.Request) {}
func (s *FileService) UnCompress(w http.ResponseWriter, r *http.Request) {}
-func (s *FileService) Search(w http.ResponseWriter, r *http.Request) {}
-
func (s *FileService) List(w http.ResponseWriter, r *http.Request) {}
diff --git a/pkg/io/path.go b/pkg/io/path.go
index 0ac8c9c4..cb3e874b 100644
--- a/pkg/io/path.go
+++ b/pkg/io/path.go
@@ -104,55 +104,3 @@ func CountX(path string) (int64, error) {
count := len(out)
return int64(count), nil
}
-
-// Search 查找文件/文件夹
-func Search(path, keyword string, sub bool) (map[string]os.FileInfo, error) {
- paths := make(map[string]os.FileInfo)
- baseDepth := strings.Count(filepath.Clean(path), string(os.PathSeparator))
-
- err := filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- if !sub && strings.Count(p, string(os.PathSeparator)) > baseDepth+1 {
- return filepath.SkipDir
- }
- if strings.Contains(info.Name(), keyword) {
- paths[p] = info
- }
- return nil
- })
-
- return paths, err
-}
-
-// SearchX 查找文件/文件夹(find命令)
-func SearchX(path, keyword string, sub bool) (map[string]os.FileInfo, error) {
- paths := make(map[string]os.FileInfo)
-
- var out string
- var err error
- if sub {
- out, err = shell.Execf("find '%s' -name '*%s*'", path, keyword)
- } else {
- out, err = shell.Execf("find '%s' -maxdepth 1 -name '*%s*'", path, keyword)
- }
- if err != nil {
- return nil, err
- }
-
- lines := strings.Split(out, "\n")
- for _, line := range lines {
- line = strings.TrimSpace(line)
- if line == "" {
- continue
- }
- info, err := os.Stat(line)
- if err != nil {
- return nil, err
- }
- paths[line] = info
- }
-
- return paths, nil
-}
diff --git a/pkg/io/search.go b/pkg/io/search.go
new file mode 100644
index 00000000..7c5fe2bc
--- /dev/null
+++ b/pkg/io/search.go
@@ -0,0 +1,100 @@
+package io
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/tnborg/panel/pkg/shell"
+)
+
+// Search 查找文件/文件夹
+func Search(path, keyword string, sub bool) (map[string]os.FileInfo, error) {
+ paths := make(map[string]os.FileInfo)
+ baseDepth := strings.Count(filepath.Clean(path), string(os.PathSeparator))
+
+ err := filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ if !sub && strings.Count(p, string(os.PathSeparator)) > baseDepth+1 {
+ return filepath.SkipDir
+ }
+ if strings.Contains(info.Name(), keyword) {
+ paths[p] = info
+ }
+ return nil
+ })
+
+ return paths, err
+}
+
+// SearchX 查找文件/文件夹(find命令)
+func SearchX(path, keyword string, sub bool) ([]os.DirEntry, error) {
+ var out string
+ var err error
+ if sub {
+ out, err = shell.Execf("find '%s' -name '*%s*'", path, keyword)
+ } else {
+ out, err = shell.Execf("find '%s' -maxdepth 1 -name '*%s*'", path, keyword)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ var entries []os.DirEntry
+ lines := strings.Split(out, "\n")
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if line == "" || line == path {
+ continue
+ }
+ entry, err := newDirEntryFromPath(line)
+ if err != nil {
+ continue // 直接跳过,不返回错误,不然很烦人的
+ }
+ entries = append(entries, entry)
+ }
+
+ return entries, nil
+}
+
+// SearchEntry 实现 os.DirEntry 接口
+type SearchEntry struct {
+ path string
+ info os.FileInfo
+}
+
+// newDirEntryFromPath 根据文件路径创建 SearchEntry
+func newDirEntryFromPath(path string) (*SearchEntry, error) {
+ info, err := os.Stat(path)
+ if err != nil {
+ return nil, err
+ }
+ return &SearchEntry{path: path, info: info}, nil
+}
+
+// Name 返回文件基本名称
+func (d *SearchEntry) Name() string {
+ return filepath.Base(d.path)
+}
+
+// IsDir 判断是否为目录
+func (d *SearchEntry) IsDir() bool {
+ return d.info.IsDir()
+}
+
+// Type 返回文件模式类型
+func (d *SearchEntry) Type() os.FileMode {
+ return d.info.Mode().Type()
+}
+
+// Info 返回文件信息
+func (d *SearchEntry) Info() (os.FileInfo, error) {
+ return d.info, nil
+}
+
+// Path 返回文件完整路径
+func (d *SearchEntry) Path() string {
+ return d.path
+}
diff --git a/pkg/io/search_test.go b/pkg/io/search_test.go
new file mode 100644
index 00000000..20ba104c
--- /dev/null
+++ b/pkg/io/search_test.go
@@ -0,0 +1,78 @@
+package io
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+)
+
+type SearchTestSuite struct {
+ suite.Suite
+}
+
+func TestSearchTestSuite(t *testing.T) {
+ suite.Run(t, &SearchTestSuite{})
+}
+
+func (s *SearchTestSuite) SetupTest() {
+ if _, err := os.Stat("testdata"); os.IsNotExist(err) {
+ s.NoError(os.MkdirAll("testdata", 0755))
+ }
+}
+
+func (s *SearchTestSuite) TearDownTest() {
+ s.NoError(os.RemoveAll("testdata"))
+}
+
+func (s *SearchTestSuite) TestSearchX() {
+ testDir := "testdata/search_test"
+ s.NoError(os.MkdirAll(testDir, 0755))
+ s.NoError(os.MkdirAll(filepath.Join(testDir, "subdir"), 0755))
+
+ testFiles := map[string]string{
+ "test_file1.txt": "内容1",
+ "test_file2.log": "内容2",
+ "another_test.txt": "内容3",
+ "subdir/nested_test.txt": "嵌套内容",
+ "unrelated.dat": "无关内容",
+ }
+
+ for path, content := range testFiles {
+ s.NoError(Write(filepath.Join(testDir, path), content, 0644))
+ }
+
+ s.Run("正常搜索", func() {
+ entries, err := SearchX(testDir, "test", false)
+ s.NoError(err)
+
+ names := make(map[string]bool)
+ for _, entry := range entries {
+ names[entry.Name()] = true
+ s.NotEmpty(entry.Name())
+ info, err := entry.Info()
+ s.NoError(err)
+ s.NotNil(info)
+ s.Equal(entry.Type(), info.Mode().Type())
+ s.Equal(entry.IsDir(), info.IsDir())
+ }
+
+ s.True(names["test_file1.txt"])
+ s.True(names["test_file2.log"])
+ s.True(names["another_test.txt"])
+ s.False(names["nested_test.txt"]) // 不应该找到子目录中的文件
+ s.False(names["unrelated.dat"]) // 不应该找到不匹配的文件
+ })
+
+ s.Run("无匹配结果", func() {
+ entries, err := SearchX(testDir, "nonexistent", false)
+ s.NoError(err)
+ s.Empty(entries)
+ })
+
+ s.Run("路径不存在", func() {
+ _, err := SearchX("/path/does/not/exist", "test", false)
+ s.Error(err)
+ })
+}
diff --git a/web/src/api/panel/file/index.ts b/web/src/api/panel/file/index.ts
index f4e5dc47..edafe824 100644
--- a/web/src/api/panel/file/index.ts
+++ b/web/src/api/panel/file/index.ts
@@ -34,6 +34,12 @@ export default {
search: (path: string, keyword: string, sub: boolean, page: number, limit: number): any =>
http.Get('/file/search', { params: { path, keyword, sub, page, limit } }),
// 获取文件列表
- list: (path: string, page: number, limit: number, sort: string): any =>
- http.Get('/file/list', { params: { path, page, limit, sort } })
+ list: (
+ path: string,
+ keyword: string,
+ sub: boolean,
+ sort: string,
+ page: number,
+ limit: number
+ ): any => http.Get('/file/list', { params: { path, keyword, sub, sort, page, limit } })
}
diff --git a/web/src/store/modules/file/index.ts b/web/src/store/modules/file/index.ts
index ca701673..908c40f5 100644
--- a/web/src/store/modules/file/index.ts
+++ b/web/src/store/modules/file/index.ts
@@ -1,16 +1,22 @@
export interface File {
path: string
+ keyword: string
+ sub: boolean
}
export const useFileStore = defineStore('file', {
state: (): File => {
return {
- path: '/'
+ path: '/opt',
+ keyword: '',
+ sub: false
}
},
actions: {
set(info: File) {
this.path = info.path
+ this.keyword = info.keyword
+ this.sub = info.sub
}
},
persist: true
diff --git a/web/src/views/file/IndexView.vue b/web/src/views/file/IndexView.vue
index 38f604f9..d18996f0 100644
--- a/web/src/views/file/IndexView.vue
+++ b/web/src/views/file/IndexView.vue
@@ -24,7 +24,11 @@ const permission = ref(false)
-
+
('')
-const path = defineModel('path', { type: String, required: true })
+const path = defineModel('path', { type: String, required: true }) // 当前路径
+const keyword = defineModel('keyword', { type: String, default: '' }) // 搜索关键词
+const sub = defineModel('sub', { type: Boolean, default: false }) // 搜索是否包括子目录
const selected = defineModel('selected', { type: Array, default: () => [] })
const marked = defineModel('marked', { type: Array, default: () => [] })
const markedType = defineModel('markedType', { type: String, required: true })
@@ -389,7 +391,7 @@ const rowProps = (row: any) => {
}
const { loading, data, page, total, pageSize, pageCount, refresh } = usePagination(
- (page, pageSize) => file.list(path.value, page, pageSize, sort.value),
+ (page, pageSize) => file.list(path.value, keyword.value, sub.value, sort.value, page, pageSize),
{
initialData: { total: 0, list: [] },
initialPageSize: 100,
@@ -633,15 +635,21 @@ const handleSorterChange = (sorter: {
switch (sorter.order) {
case 'ascend':
sort.value = 'asc'
- refresh()
+ nextTick(() => {
+ refresh()
+ })
break
case 'descend':
sort.value = 'desc'
- refresh()
+ nextTick(() => {
+ refresh()
+ })
break
default:
sort.value = ''
- refresh()
+ nextTick(() => {
+ refresh()
+ })
break
}
}
@@ -649,15 +657,29 @@ const handleSorterChange = (sorter: {
}
onMounted(() => {
+ // 监听路径变化并刷新列表
watch(
path,
() => {
selected.value = []
- refresh()
- window.$bus.emit('push-history', path.value)
+ keyword.value = ''
+ sub.value = false
+ nextTick(() => {
+ refresh()
+ })
+ window.$bus.emit('file:push-history', path.value)
},
{ immediate: true }
)
+ // 监听搜索事件
+ window.$bus.on('file:search', () => {
+ selected.value = []
+ nextTick(() => {
+ refresh()
+ })
+ window.$bus.emit('file:push-history', path.value)
+ })
+ // 监听刷新事件
window.$bus.on('file:refresh', refresh)
})
diff --git a/web/src/views/file/PathInput.vue b/web/src/views/file/PathInput.vue
index 55f7f181..8185efc0 100644
--- a/web/src/views/file/PathInput.vue
+++ b/web/src/views/file/PathInput.vue
@@ -3,10 +3,11 @@ import type { InputInst } from 'naive-ui'
import { useGettext } from 'vue3-gettext'
import { checkPath } from '@/utils/file'
-import SearchModal from '@/views/file/SearchModal.vue'
const { $gettext } = useGettext()
-const path = defineModel('path', { type: String, required: true })
+const path = defineModel('path', { type: String, required: true }) // 当前路径
+const keyword = defineModel('keyword', { type: String, default: '' }) // 搜索关键词
+const sub = defineModel('sub', { type: Boolean, default: false }) // 搜索是否包括子目录
const isInput = ref(false)
const pathInput = ref(null)
const input = ref('www')
@@ -14,12 +15,6 @@ const input = ref('www')
const history: string[] = []
let current = -1
-const searchShow = ref(false)
-const search = ref({
- keyword: '',
- sub: false
-})
-
const handleInput = () => {
isInput.value = true
nextTick(() => {
@@ -92,7 +87,7 @@ const handlePushHistory = (path: string) => {
}
const handleSearch = () => {
- searchShow.value = true
+ window.$bus.emit('file:search')
}
watch(
@@ -104,11 +99,11 @@ watch(
)
onMounted(() => {
- window.$bus.on('push-history', handlePushHistory)
+ window.$bus.on('file:push-history', handlePushHistory)
})
onUnmounted(() => {
- window.$bus.off('push-history')
+ window.$bus.off('file:push-history')
})
@@ -151,9 +146,9 @@ onUnmounted(() => {
/>
-
+
-
+
{{ $gettext('Include subdirectories') }}
@@ -163,12 +158,6 @@ onUnmounted(() => {
-
diff --git a/web/src/views/file/SearchModal.vue b/web/src/views/file/SearchModal.vue
deleted file mode 100644
index 0beedeb9..00000000
--- a/web/src/views/file/SearchModal.vue
+++ /dev/null
@@ -1,178 +0,0 @@
-
-
-
-
-
-
-
-
-