Administrator 1 년 전
커밋
f7d2ad9afb
13개의 변경된 파일634개의 추가작업 그리고 0개의 파일을 삭제
  1. 6 0
      .gitignore
  2. 59 0
      README.md
  3. 34 0
      Taskfile.yml
  4. 16 0
      dao/KvStore.go
  5. 15 0
      data/Entity.go
  6. 25 0
      data/Window.go
  7. 89 0
      db/Sqlite.go
  8. 72 0
      go.mod
  9. 153 0
      handler/FileUpload.go
  10. 16 0
      handler/FileUpload_test.go
  11. 60 0
      handler/Service.go
  12. 9 0
      handler/login.go
  13. 80 0
      main.go

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+.task
+bin
+frontend/dist
+frontend/node_modules
+build/linux/appimage/build
+build/windows/nsis/MicrosoftEdgeWebview2Setup.exe

+ 59 - 0
README.md

@@ -0,0 +1,59 @@
+# Welcome to Your New Wails3 Project!
+
+Congratulations on generating your Wails3 application! This README will guide you through the next steps to get your project up and running.
+
+## Getting Started
+
+1. Navigate to your project directory in the terminal.
+
+2. To run your application in development mode, use the following command:
+
+   ```
+   wails3 dev
+   ```
+
+   This will start your application and enable hot-reloading for both frontend and backend changes.
+
+3. To build your application for production, use:
+
+   ```
+   wails3 build
+   ```
+
+   This will create a production-ready executable in the `build` directory.
+
+## Exploring Wails3 Features
+
+Now that you have your project set up, it's time to explore the features that Wails3 offers:
+
+1. **Check out the examples**: The best way to learn is by example. Visit the `examples` directory in the `v3/examples` directory to see various sample applications.
+
+2. **Run an example**: To run any of the examples, navigate to the example's directory and use:
+
+   ```
+   go run .
+   ```
+
+   Note: Some examples may be under development during the alpha phase.
+
+3. **Explore the documentation**: Visit the [Wails3 documentation](https://v3alpha.wails.io/) for in-depth guides and API references.
+
+4. **Join the community**: Have questions or want to share your progress? Join the [Wails Discord](https://discord.gg/JDdSxwjhGf) or visit the [Wails discussions on GitHub](https://github.com/wailsapp/wails/discussions).
+
+## Project Structure
+
+Take a moment to familiarize yourself with your project structure:
+
+- `frontend/`: Contains your frontend code (HTML, CSS, JavaScript/TypeScript)
+- `main.go`: The entry point of your Go backend
+- `app.go`: Define your application structure and methods here
+- `wails.json`: Configuration file for your Wails project
+
+## Next Steps
+
+1. Modify the frontend in the `frontend/` directory to create your desired UI.
+2. Add backend functionality in `main.go`.
+3. Use `wails3 dev` to see your changes in real-time.
+4. When ready, build your application with `wails3 build`.
+
+Happy coding with Wails3! If you encounter any issues or have questions, don't hesitate to consult the documentation or reach out to the Wails community.

+ 34 - 0
Taskfile.yml

@@ -0,0 +1,34 @@
+version: '3'
+
+includes:
+  common: ./build/Taskfile.yml
+  windows: ./build/windows/Taskfile.yml
+  darwin: ./build/darwin/Taskfile.yml
+  linux: ./build/linux/Taskfile.yml
+
+vars:
+  APP_NAME: "file-manage-file"
+  BIN_DIR: "bin"
+  VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}'
+
+tasks:
+  build:
+    summary: Builds the application
+    cmds:
+      - task: "{{OS}}:build"
+
+  package:
+    summary: Packages a production build of the application
+    cmds:
+      - task: "{{OS}}:package"
+
+  run:
+    summary: Runs the application
+    cmds:
+      - task: "{{OS}}:run"
+
+  dev:
+    summary: Runs the application in development mode
+    cmds:
+      - wails3 dev -config ./build/config.yml -port {{.VITE_PORT}}
+

+ 16 - 0
dao/KvStore.go

@@ -0,0 +1,16 @@
+package dao
+
+import (
+	"file-manage-ui/data"
+	"file-manage-ui/db"
+)
+
+type KvStoreDao struct {
+}
+
+func (dao *KvStoreDao) KvStoreDao(key string) (data.KVStore, error) {
+	var kv data.KVStore
+	_, err := db.DB.Table(kv.TableName()).Where("key = ?", key).Get(&kv)
+	return kv, err
+
+}

+ 15 - 0
data/Entity.go

@@ -0,0 +1,15 @@
+package data
+
+type EntityInterface interface {
+	TableName() string
+}
+
+type KVStore struct {
+	Key   string `json:"key" xorm:"'key' pk"`  // 主键字段
+	Value string `json:"value" xorm:"'value'"` // 值字段
+}
+
+// TableName 设置form表名
+func (KVStore) TableName() string {
+	return "kv_store"
+}

+ 25 - 0
data/Window.go

@@ -0,0 +1,25 @@
+package data
+
+import (
+	"fmt"
+	"github.com/wailsapp/wails/v3/pkg/application"
+	"github.com/wailsapp/wails/v3/pkg/events"
+)
+
+// WindowMap 窗口管理
+var WindowMap = make(map[*application.WebviewWindow]bool)
+
+// App App应用
+var App *application.App
+
+func WindowManager(app *application.App) {
+	App = app
+	App.OnWindowCreation(func(window application.Window) {
+		// 窗口管理
+		WindowMap[window.(*application.WebviewWindow)] = true
+		window.OnWindowEvent(events.Common.WindowClosing, func(e *application.WindowEvent) {
+			fmt.Println("窗口关闭:", window.Name())
+		})
+	})
+
+}

+ 89 - 0
db/Sqlite.go

@@ -0,0 +1,89 @@
+package db
+
+import (
+	"file-manage-ui/data"
+	_ "github.com/glebarez/sqlite"
+	"log"
+	"xorm.io/xorm"
+)
+
+var DB *xorm.Engine
+
+func init() {
+	Open()
+}
+func Open() {
+	db, err := xorm.NewEngine("sqlite", "./file/email.db")
+	if err != nil {
+		log.Fatal(err)
+	}
+	//创建表
+	err = db.Sync2(new(data.KVStore)) //new(entity.Email), new(entity.User), new(entity.FromEmail), new(entity.Code)
+	if err != nil {
+		log.Fatalf("同步表结构时出错: %s", err)
+	}
+	DB = db
+	DB.ShowSQL(true)
+	DB.Logger().ShowSQL(true)
+}
+func Close() {
+	defer DB.Close()
+}
+
+//func InsertEmail(email entity.Email) {
+//	_, err := DB.Insert(&email)
+//	if err != nil {
+//		log.Fatalf("插入数据时出错: %s", err)
+//	}
+//	fmt.Println("数据插入成功")
+//}
+//
+//func GetEmails() {
+//	var emails []entity.Email
+//	err := DB.Find(&emails)
+//	if err != nil {
+//		log.Fatal(err)
+//	}
+//
+//	for _, email := range emails {
+//		fmt.Printf("%+v\n", email)
+//	}
+//}
+//func GetEmailById() {
+//	var emails []entity.Email
+//	err := DB.ID(&emails)
+//	if err != nil {
+//		log.Fatal(err)
+//	}
+//
+//	for _, email := range emails {
+//		fmt.Printf("%+v\n", email)
+//	}
+//}
+//
+//func UpdateEmail(id int64, newEmail string) {
+//	email := new(entity.Email)
+//	has, err := DB.ID(id).Get(email)
+//	if err != nil {
+//		log.Fatal(err)
+//	}
+//	if !has {
+//		fmt.Println("数据未找到")
+//		return
+//	}
+//
+//	email.Email = newEmail
+//	_, err = DB.ID(id).Update(email)
+//	if err != nil {
+//		log.Fatalf("更新数据时出错: %s", err)
+//	}
+//	fmt.Println("数据更新成功")
+//}
+//
+//func DeleteEmail(id int64) {
+//	_, err := DB.ID(id).Delete(&entity.Email{})
+//	if err != nil {
+//		log.Fatalf("删除数据时出错: %s", err)
+//	}
+//	fmt.Println("数据删除成功")
+//}

+ 72 - 0
go.mod

@@ -0,0 +1,72 @@
+module file-manage-ui
+
+go 1.23.4
+
+require (
+	github.com/glebarez/sqlite v1.11.0
+	github.com/wailsapp/wails/v3 v3.0.0-alpha.9
+	xorm.io/xorm v1.3.9
+)
+
+require (
+	dario.cat/mergo v1.0.1 // indirect
+	github.com/Microsoft/go-winio v0.6.1 // indirect
+	github.com/ProtonMail/go-crypto v1.1.5 // indirect
+	github.com/adrg/xdg v0.5.0 // indirect
+	github.com/bep/debounce v1.2.1 // indirect
+	github.com/cloudflare/circl v1.3.8 // indirect
+	github.com/cyphar/filepath-securejoin v0.3.6 // indirect
+	github.com/dustin/go-humanize v1.0.1 // indirect
+	github.com/ebitengine/purego v0.4.0-alpha.4 // indirect
+	github.com/emirpasic/gods v1.18.1 // indirect
+	github.com/glebarez/go-sqlite v1.21.2 // indirect
+	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
+	github.com/go-git/go-billy/v5 v5.6.2 // indirect
+	github.com/go-git/go-git/v5 v5.13.2 // indirect
+	github.com/go-ole/go-ole v1.3.0 // indirect
+	github.com/goccy/go-json v0.8.1 // indirect
+	github.com/godbus/dbus/v5 v5.1.0 // indirect
+	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+	github.com/golang/snappy v0.0.4 // indirect
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
+	github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.5 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/kevinburke/ssh_config v1.2.0 // indirect
+	github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
+	github.com/leaanthony/u v1.1.1 // indirect
+	github.com/lmittmann/tint v1.0.4 // indirect
+	github.com/matryer/is v1.4.1 // indirect
+	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/pjbgf/sha1cd v0.3.2 // indirect
+	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
+	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
+	github.com/rivo/uniseg v0.4.7 // indirect
+	github.com/samber/lo v1.49.1 // indirect
+	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
+	github.com/skeema/knownhosts v1.3.0 // indirect
+	github.com/syndtr/goleveldb v1.0.0 // indirect
+	github.com/wailsapp/go-webview2 v1.0.19 // indirect
+	github.com/wailsapp/mimetype v1.4.1 // indirect
+	github.com/xanzy/ssh-agent v0.3.3 // indirect
+	golang.org/x/crypto v0.33.0 // indirect
+	golang.org/x/mod v0.23.0 // indirect
+	golang.org/x/net v0.35.0 // indirect
+	golang.org/x/sync v0.11.0 // indirect
+	golang.org/x/sys v0.30.0 // indirect
+	golang.org/x/text v0.22.0 // indirect
+	golang.org/x/tools v0.30.0 // indirect
+	gopkg.in/ini.v1 v1.67.0 // indirect
+	gopkg.in/warnings.v0 v0.1.2 // indirect
+	gorm.io/gorm v1.25.7 // indirect
+	modernc.org/libc v1.22.5 // indirect
+	modernc.org/mathutil v1.5.0 // indirect
+	modernc.org/memory v1.5.0 // indirect
+	modernc.org/sqlite v1.23.1 // indirect
+	xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 // indirect
+)

+ 153 - 0
handler/FileUpload.go

@@ -0,0 +1,153 @@
+package handler
+
+import (
+	"errors"
+	"fmt"
+	"io/fs"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+)
+
+type PathInfo struct {
+	Dirs  []string // 目录路径(包含所有层级)
+	Files []string // 文件路径
+}
+
+var pathPool = sync.Pool{
+	New: func() interface{} {
+		return &PathInfo{
+			Dirs:  make([]string, 0, 1024), // 预分配容量
+			Files: make([]string, 0, 4096),
+		}
+	},
+}
+
+func GoHeavy(info *PathInfo) (PathInfo, error) {
+	m := make(map[string]bool)
+	for i := range info.Files {
+		path := info.Files[i]
+		lastIndex := strings.LastIndex(path, "/")
+
+		if lastIndex == -1 {
+			lastIndex = strings.LastIndex(path, "\\")
+		}
+		if lastIndex == -1 {
+			continue
+		}
+		//if _, ok := m[path[:lastIndex]]; !ok {
+		//	fmt.Println(path[:lastIndex])
+		//}
+		m[path[:lastIndex]] = true
+	}
+	var dirs = make([]string, 0, len(m))
+	fmt.Println(len(info.Dirs))
+	for i := range info.Dirs {
+		path := info.Dirs[i]
+		if !m[path] {
+			fmt.Println(info.Dirs[i])
+			dirs = append(dirs, info.Dirs[i])
+		}
+	}
+	info.Dirs = dirs
+	return *info, nil
+}
+
+func GetPathInfo(root string) (*PathInfo, error) {
+	//获取文件名
+	index := strings.LastIndex(root, "/")
+	if index == -1 {
+		index = strings.LastIndex(root, "\\")
+	}
+	//RootFileName := ""
+	//if index == -1 {
+	//	RootFileName = root
+	//} else {
+	//	RootFileName = root[:index+1]
+	//}
+
+	// 复用对象减少内存分配[9](@ref)
+	info := pathPool.Get().(*PathInfo)
+
+	// 路径有效性校验[5,6](@ref)
+	root = filepath.Clean(root)
+	stat, err := os.Stat(root)
+	if err != nil {
+		if errors.Is(err, fs.ErrNotExist) {
+			return nil, &os.PathError{Op: "stat", Path: root, Err: err} // 增强错误信息[8](@ref)
+		}
+		return nil, err
+	}
+
+	// 文件直接返回[4](@ref)
+	if !stat.IsDir() {
+		info.Files = append(info.Files, root)
+		return info, nil
+	}
+
+	// 使用WalkDir优化遍历性能[4,9](@ref)
+	err = filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			// 处理遍历错误但继续执行[7](@ref)
+			if errors.Is(err, fs.ErrPermission) {
+				return nil // 跳过权限错误
+			}
+			return err
+		}
+
+		// 排除根目录自身
+		if path == root {
+			return nil
+		}
+
+		// 动态扩容检测[9](@ref)
+		if len(info.Dirs)+len(info.Files) > 1e8 {
+			return errors.New("超出内存安全阈值(1,000,000条路径)")
+		}
+		//fmt.Println(path[len(RootFileName):])
+		if d.IsDir() {
+			info.Dirs = append(info.Dirs, path)
+		} else {
+			info.Files = append(info.Files, path)
+		}
+		return nil
+	})
+
+	if err != nil {
+		return nil, &os.PathError{Op: "walk", Path: root, Err: err} // 错误包装[6](@ref)
+	}
+
+	return info, nil
+}
+
+// 流式处理版本(适用于超大数据集)
+func StreamPathInfo(root string) (<-chan string, <-chan error) {
+	paths := make(chan string, 1000)
+	errs := make(chan error, 1)
+
+	go func() {
+		defer close(paths)
+		defer close(errs)
+
+		err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
+			if err != nil {
+				return err
+			}
+			if path != root {
+				select {
+				case paths <- path:
+				default:
+					return errors.New("消费者处理过慢")
+				}
+			}
+			return nil
+		})
+
+		if err != nil {
+			errs <- &os.PathError{Op: "stream", Path: root, Err: err}
+		}
+	}()
+
+	return paths, errs
+}

+ 16 - 0
handler/FileUpload_test.go

@@ -0,0 +1,16 @@
+package handler
+
+import "testing"
+
+func TestFileTest(t *testing.T) {
+	info, err := GetPathInfo("D:\\")
+	if err != nil {
+		t.Error(err)
+	}
+	heavy, err := GoHeavy(info)
+	if err != nil {
+		t.Error(err)
+	}
+	t.Log(heavy.Dirs)
+
+}

+ 60 - 0
handler/Service.go

@@ -0,0 +1,60 @@
+package handler
+
+import (
+	"context"
+	"file-manage-ui/data"
+	"github.com/wailsapp/wails/v3/pkg/application"
+)
+
+type Ctx struct {
+	ctx context.Context
+}
+
+// Service struct
+type Service struct {
+	//ctx context.Context
+	Ctx *Ctx
+}
+
+// NewService creates a new App application struct
+func NewService(Ctx *Ctx) *Service {
+	return &Service{Ctx: Ctx}
+
+}
+
+// Startup is called when the app starts. The context is saved,
+// so we can call the runtime methods
+func (a *Ctx) Startup(ctx context.Context) {
+	a.ctx = ctx
+}
+
+// CloseApp 根据名称关闭窗口
+func (a *Service) CloseApp(winName string) {
+	if winName == "login-page" {
+
+	}
+}
+func (a *Service) OpenLoginPage() {
+	LoginWindow(data.App)
+}
+
+func LoginWindow(app *application.App) {
+	//窗口管理
+	win1 := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
+		Title: "Login",
+		Name:  "login",
+		Mac: application.MacWindow{
+			InvisibleTitleBarHeight: 50,
+			Backdrop:                application.MacBackdropTranslucent,
+			TitleBar:                application.MacTitleBarHiddenInset,
+		},
+		Frameless: true,
+		Windows:   application.WindowsWindow{},
+
+		//BackgroundColour: application.NewRGB(27, 38, 54),
+		URL:               "/#/login",
+		EnableDragAndDrop: true,
+	})
+	// 加载窗口到 管理器中
+	data.WindowMap[win1] = true
+}

+ 9 - 0
handler/login.go

@@ -0,0 +1,9 @@
+package handler
+
+type LoginHandler struct {
+	Ctx *Ctx
+}
+
+func (l *LoginHandler) IsLogin() {
+
+}

+ 80 - 0
main.go

@@ -0,0 +1,80 @@
+package main
+
+import (
+	"embed"
+	_ "embed"
+	"file-manage-ui/data"
+	"file-manage-ui/handler"
+	"fmt"
+	"github.com/wailsapp/wails/v3/pkg/events"
+	"log"
+	"time"
+
+	"github.com/wailsapp/wails/v3/pkg/application"
+)
+
+//go:embed all:frontend/dist
+var assets embed.FS
+
+func main() {
+	a := &handler.Service{}
+	app := application.New(application.Options{
+		Name:        "文件管理系统",
+		Description: "上传文件管理系统",
+		//功能服务
+		Services: []application.Service{
+			//application.NewService(&GreetService{}),
+			application.NewService(a),
+			application.NewService(&handler.LoginHandler{}),
+		},
+		//资源文件配置
+		Assets: application.AssetOptions{
+			Handler: application.AssetFileServerFS(assets),
+		},
+		Mac: application.MacOptions{
+			ApplicationShouldTerminateAfterLastWindowClosed: true,
+		},
+	})
+	data.WindowManager(app)
+	MainWindow(app)
+	go func() {
+		for {
+			now := time.Now().Format(time.RFC1123)
+			app.EmitEvent("time", now)
+			time.Sleep(time.Second)
+		}
+	}()
+	err := app.Run()
+
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+func MainWindow(app *application.App) {
+	//窗口管理
+	win1 := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
+		Title: "Main",
+		Name:  "main",
+		Mac: application.MacWindow{
+			InvisibleTitleBarHeight: 50,
+			Backdrop:                application.MacBackdropTranslucent,
+			TitleBar:                application.MacTitleBarHiddenInset,
+		},
+		Frameless: true,
+		Windows:   application.WindowsWindow{},
+		//BackgroundColour: application.NewRGB(27, 38, 54),
+		URL:               "/",
+		EnableDragAndDrop: true,
+	})
+
+	// 加载窗口到 管理器中
+	data.WindowMap[win1] = true
+
+	win1.OnWindowEvent(events.Common.WindowFilesDropped, func(e *application.WindowEvent) {
+		app.Logger.Info("WindowFilesDropped")
+		for index, item := range e.Context().DroppedFiles() {
+			app.Logger.Info(fmt.Sprintf("文件路径(%d): %s", index, item))
+		}
+	})
+}