新增:文件系统导航面包屑
功能: - 新增 PathBreadcrumb 组件,支持路径快速跳转 - 新增 DropdownItem 通用下拉菜单组件 优化: - 版本升级流程优化(Pinia 状态管理、进度节流、完整下载验证) - 模块延迟初始化(数据库、文件系统按需启动) - API 数据格式统一(蛇形转驼峰) - CodeMirror 语言包按需动态加载 - Markdown 渲染增强(支持锚点跳转) 重构: - 迁移到 Pinia 状态管理(stores/config.ts、stores/theme.ts、stores/update.ts) - 简化 UpdatePanel、UpdateNotification、ThemeToggle 逻辑 - 优化表结构加载逻辑 清理: - 删除测试组件 index-simple.vue - 删除旧的 useTheme.ts
This commit is contained in:
@@ -50,13 +50,6 @@ func (api *UpdateAPI) CheckUpdate() (map[string]interface{}, error) {
|
||||
// GetCurrentVersion 获取当前版本号
|
||||
func (api *UpdateAPI) GetCurrentVersion() (map[string]interface{}, error) {
|
||||
version := service.GetCurrentVersion()
|
||||
|
||||
// 同步配置中的版本号
|
||||
if config, err := service.LoadUpdateConfig(); err == nil && config.CurrentVersion != version {
|
||||
config.CurrentVersion = version
|
||||
service.SaveUpdateConfig(config)
|
||||
}
|
||||
|
||||
return successResponse(map[string]interface{}{
|
||||
"version": version,
|
||||
}), nil
|
||||
@@ -69,13 +62,6 @@ func (api *UpdateAPI) GetUpdateConfig() (map[string]interface{}, error) {
|
||||
return errorResponse(err.Error()), nil
|
||||
}
|
||||
|
||||
// 同步最新版本号
|
||||
latestVersion := service.GetCurrentVersion()
|
||||
if config.CurrentVersion != latestVersion {
|
||||
config.CurrentVersion = latestVersion
|
||||
service.SaveUpdateConfig(config)
|
||||
}
|
||||
|
||||
return successResponse(map[string]interface{}{
|
||||
"current_version": config.CurrentVersion,
|
||||
"last_check_time": config.LastCheckTime.Format("2006-01-02 15:04:05"),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -10,12 +11,14 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LocalFileServer 本地文件服务器(独立的 HTTP 服务器)
|
||||
type LocalFileServer struct {
|
||||
server *http.Server
|
||||
addr string
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -258,3 +261,35 @@ func HandleLocalFile(w http.ResponseWriter, r *http.Request) {
|
||||
func isAllowedFileType(ext string) bool {
|
||||
return defaultFileTypeManager.IsAllowed(ext)
|
||||
}
|
||||
|
||||
// Shutdown 优雅关闭文件服务器
|
||||
func (lfs *LocalFileServer) Shutdown() error {
|
||||
if lfs == nil || lfs.server == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
lfs.mu.Lock()
|
||||
defer lfs.mu.Unlock()
|
||||
|
||||
// 创建带超时的上下文
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
log.Printf("[LocalFileServer] 正在关闭...")
|
||||
|
||||
if err := lfs.server.Shutdown(ctx); err != nil {
|
||||
log.Printf("[LocalFileServer] 关闭失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[LocalFileServer] 已关闭")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShutdownLocalFileServer 关闭全局文件服务器
|
||||
func ShutdownLocalFileServer() error {
|
||||
if localFileServer != nil {
|
||||
return localFileServer.Shutdown()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ func (s *FileSystemService) DeletePathWithContext(ctx context.Context, path stri
|
||||
|
||||
// 返回被删除的文件信息,用于前端更新
|
||||
return &FileOperationResult{
|
||||
Path: path,
|
||||
Path: filepath.ToSlash(path), // 统一使用正斜杠
|
||||
Name: info.Name(),
|
||||
Size: info.Size(),
|
||||
SizeStr: formatBytes(info.Size()),
|
||||
@@ -297,7 +297,7 @@ func (s *FileSystemService) ListDir(path string) ([]map[string]interface{}, erro
|
||||
fullPath := filepath.Join(path, entry.Name())
|
||||
result = append(result, map[string]interface{}{
|
||||
"name": entry.Name(),
|
||||
"path": fullPath,
|
||||
"path": filepath.ToSlash(fullPath), // 统一使用正斜杠
|
||||
"is_dir": entry.IsDir(),
|
||||
"size": info.Size(),
|
||||
"mod_time": info.ModTime().Format("2006-01-02 15:04:05"),
|
||||
@@ -338,14 +338,14 @@ func (s *FileSystemService) CreateDir(path string) (*FileOperationResult, error)
|
||||
if err != nil {
|
||||
// 创建成功但获取信息失败,返回基本信息
|
||||
return &FileOperationResult{
|
||||
Path: path,
|
||||
Path: filepath.ToSlash(path), // 统一使用正斜杠
|
||||
Name: filepath.Base(path),
|
||||
IsDir: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &FileOperationResult{
|
||||
Path: path,
|
||||
Path: filepath.ToSlash(path), // 统一使用正斜杠
|
||||
Name: info.Name(),
|
||||
Size: info.Size(),
|
||||
SizeStr: formatBytes(info.Size()),
|
||||
@@ -385,7 +385,7 @@ func (s *FileSystemService) CreateFile(path string) (*FileOperationResult, error
|
||||
if err != nil {
|
||||
// 创建成功但获取信息失败,返回基本信息
|
||||
return &FileOperationResult{
|
||||
Path: path,
|
||||
Path: filepath.ToSlash(path), // 统一使用正斜杠
|
||||
Name: filepath.Base(path),
|
||||
IsDir: false,
|
||||
Size: 0,
|
||||
@@ -393,7 +393,7 @@ func (s *FileSystemService) CreateFile(path string) (*FileOperationResult, error
|
||||
}
|
||||
|
||||
return &FileOperationResult{
|
||||
Path: path,
|
||||
Path: filepath.ToSlash(path), // 统一使用正斜杠
|
||||
Name: info.Name(),
|
||||
Size: info.Size(),
|
||||
SizeStr: formatBytes(info.Size()),
|
||||
@@ -424,7 +424,7 @@ func (s *FileSystemService) GetFileInfo(path string) (map[string]interface{}, er
|
||||
|
||||
return map[string]interface{}{
|
||||
"name": info.Name(),
|
||||
"path": path,
|
||||
"path": filepath.ToSlash(path), // 统一使用正斜杠
|
||||
"size": info.Size(),
|
||||
"size_str": formatBytes(info.Size()),
|
||||
"is_dir": info.IsDir(),
|
||||
@@ -472,21 +472,21 @@ func (s *FileSystemService) RenamePath(oldPath, newPath string) (*FileOperationR
|
||||
if err != nil {
|
||||
// 重命名成功但获取信息失败,返回基本信息
|
||||
return &FileOperationResult{
|
||||
Path: newPath,
|
||||
Path: filepath.ToSlash(newPath), // 统一使用正斜杠
|
||||
Name: filepath.Base(newPath),
|
||||
OldPath: oldPath,
|
||||
OldPath: filepath.ToSlash(oldPath),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &FileOperationResult{
|
||||
Path: newPath,
|
||||
Path: filepath.ToSlash(newPath), // 统一使用正斜杠
|
||||
Name: info.Name(),
|
||||
Size: info.Size(),
|
||||
SizeStr: formatBytes(info.Size()),
|
||||
IsDir: info.IsDir(),
|
||||
ModTime: info.ModTime().Format("2006-01-02 15:04:05"),
|
||||
Mode: info.Mode().String(),
|
||||
OldPath: oldPath,
|
||||
OldPath: filepath.ToSlash(oldPath),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ func ListZipContents(zipPath string) ([]map[string]interface{}, error) {
|
||||
|
||||
entry := map[string]interface{}{
|
||||
"name": name,
|
||||
"path": file.Name, // zip 中的完整路径
|
||||
"path": file.Name, // zip 中的完整路径(已使用 /)
|
||||
"is_dir": isDir,
|
||||
"size": file.UncompressedSize64,
|
||||
"compressed": file.CompressedSize64,
|
||||
|
||||
@@ -103,7 +103,7 @@ func getCompressionMethodString(method uint16) string {
|
||||
func createFileInfoMap(file *zip.File, includeExtra ...bool) map[string]interface{} {
|
||||
info := map[string]interface{}{
|
||||
"name": filepath.Base(file.Name),
|
||||
"path": file.Name,
|
||||
"path": file.Name, // zip 中的路径(已使用 /)
|
||||
"is_dir": file.Mode().IsDir(),
|
||||
"size": file.UncompressedSize64,
|
||||
"compressed": file.CompressedSize64,
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -62,20 +61,13 @@ func NewUpdateService(checkURL string) *UpdateService {
|
||||
|
||||
// CheckUpdate 检查更新
|
||||
func (s *UpdateService) CheckUpdate() (*UpdateCheckResult, error) {
|
||||
log.Printf("[更新检查] 开始检查更新,检查地址: %s", s.checkURL)
|
||||
|
||||
config, err := LoadUpdateConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("加载配置失败: %v", err)
|
||||
}
|
||||
|
||||
// 同步版本号
|
||||
currentVersionStr, err := s.syncConfigVersion(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
currentVersion, err := ParseVersion(currentVersionStr)
|
||||
// 获取当前版本(使用缓存)
|
||||
currentVersion, err := ParseVersion(GetCurrentVersion())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析当前版本失败: %v", err)
|
||||
}
|
||||
@@ -86,14 +78,6 @@ func (s *UpdateService) CheckUpdate() (*UpdateCheckResult, error) {
|
||||
return nil, fmt.Errorf("获取远程版本信息失败: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("[更新检查] 远程版本信息: 版本=%s, 下载地址=%s, 强制更新=%v, 更新日志长度=%d",
|
||||
remoteInfo.Version, remoteInfo.DownloadURL, remoteInfo.ForceUpdate, len(remoteInfo.Changelog))
|
||||
if remoteInfo.Changelog != "" {
|
||||
log.Printf("[更新检查] 更新日志内容: %s", remoteInfo.Changelog)
|
||||
} else {
|
||||
log.Printf("[更新检查] 警告: 远程接口未返回更新日志")
|
||||
}
|
||||
|
||||
// 解析远程版本号
|
||||
remoteVersion, err := ParseVersion(remoteInfo.Version)
|
||||
if err != nil {
|
||||
@@ -102,55 +86,30 @@ func (s *UpdateService) CheckUpdate() (*UpdateCheckResult, error) {
|
||||
|
||||
// 比较版本
|
||||
hasUpdate := remoteVersion.IsNewerThan(currentVersion)
|
||||
log.Printf("[更新检查] 版本比较: 当前=%s, 远程=%s, 有更新=%v",
|
||||
currentVersion.String(), remoteVersion.String(), hasUpdate)
|
||||
|
||||
// 更新最后检查时间
|
||||
config.UpdateLastCheckTime()
|
||||
|
||||
result := &UpdateCheckResult{
|
||||
return &UpdateCheckResult{
|
||||
HasUpdate: hasUpdate,
|
||||
CurrentVersion: currentVersionStr,
|
||||
CurrentVersion: GetCurrentVersion(),
|
||||
LatestVersion: remoteInfo.Version,
|
||||
DownloadURL: remoteInfo.DownloadURL,
|
||||
Changelog: remoteInfo.Changelog,
|
||||
ForceUpdate: remoteInfo.ForceUpdate,
|
||||
ReleaseDate: remoteInfo.ReleaseDate,
|
||||
FileSize: remoteInfo.FileSize,
|
||||
}
|
||||
|
||||
log.Printf("[更新检查] 检查完成: 有更新=%v", hasUpdate)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// syncConfigVersion 同步配置中的版本号
|
||||
func (s *UpdateService) syncConfigVersion(config *UpdateConfig) (string, error) {
|
||||
currentVersionStr := GetCurrentVersion()
|
||||
if currentVersionStr == "" {
|
||||
currentVersionStr = config.CurrentVersion
|
||||
log.Printf("[更新检查] 使用配置中的版本号: %s", currentVersionStr)
|
||||
} else if config.CurrentVersion != currentVersionStr {
|
||||
log.Printf("[更新检查] 配置中的版本号 (%s) 与当前版本号 (%s) 不一致,更新配置",
|
||||
config.CurrentVersion, currentVersionStr)
|
||||
config.CurrentVersion = currentVersionStr
|
||||
if err := SaveUpdateConfig(config); err != nil {
|
||||
log.Printf("[更新检查] 更新配置失败: %v", err)
|
||||
}
|
||||
}
|
||||
return currentVersionStr, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
// fetchRemoteVersionInfo 获取远程版本信息
|
||||
func (s *UpdateService) fetchRemoteVersionInfo() (*RemoteVersionInfo, error) {
|
||||
if s.checkURL == "" {
|
||||
log.Printf("[远程版本] 版本检查 URL 未配置")
|
||||
return nil, fmt.Errorf("版本检查 URL 未配置,请先设置检查地址")
|
||||
}
|
||||
|
||||
log.Printf("[远程版本] 请求远程版本信息: %s", s.checkURL)
|
||||
|
||||
// 添加时间戳参数防止缓存
|
||||
timestamp := time.Now().UnixMilli() // 使用毫秒级时间戳
|
||||
timestamp := time.Now().UnixMilli()
|
||||
var requestURL string
|
||||
if strings.Contains(s.checkURL, "?") {
|
||||
requestURL = fmt.Sprintf("%s&t=%d", s.checkURL, timestamp)
|
||||
@@ -158,8 +117,6 @@ func (s *UpdateService) fetchRemoteVersionInfo() (*RemoteVersionInfo, error) {
|
||||
requestURL = fmt.Sprintf("%s?t=%d", s.checkURL, timestamp)
|
||||
}
|
||||
|
||||
log.Printf("[远程版本] 实际请求URL: %s", requestURL)
|
||||
|
||||
// 创建 HTTP 客户端,设置超时
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
@@ -168,12 +125,10 @@ func (s *UpdateService) fetchRemoteVersionInfo() (*RemoteVersionInfo, error) {
|
||||
// 发送请求
|
||||
resp, err := client.Get(requestURL)
|
||||
if err != nil {
|
||||
log.Printf("[远程版本] 网络请求失败: %v", err)
|
||||
return nil, fmt.Errorf("网络请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
log.Printf("[远程版本] HTTP 响应状态码: %d", resp.StatusCode)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("服务器返回错误状态码: %d", resp.StatusCode)
|
||||
}
|
||||
@@ -181,25 +136,19 @@ func (s *UpdateService) fetchRemoteVersionInfo() (*RemoteVersionInfo, error) {
|
||||
// 读取响应
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Printf("[远程版本] 读取响应失败: %v", err)
|
||||
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("[远程版本] 响应内容长度: %d 字节", len(body))
|
||||
|
||||
// 解析 JSON
|
||||
var remoteInfo RemoteVersionInfo
|
||||
if err := json.Unmarshal(body, &remoteInfo); err != nil {
|
||||
log.Printf("[远程版本] 解析 JSON 失败: %v, 响应内容: %s", err, string(body))
|
||||
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
if remoteInfo.Version == "" {
|
||||
log.Printf("[远程版本] 远程版本信息不完整,响应内容: %s", string(body))
|
||||
return nil, fmt.Errorf("远程版本信息不完整")
|
||||
}
|
||||
|
||||
log.Printf("[远程版本] 成功获取远程版本信息: %+v", remoteInfo)
|
||||
return &remoteInfo, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package service
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
@@ -71,20 +70,16 @@ func LoadUpdateConfig() (*UpdateConfig, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 同步最新版本号
|
||||
latestVersion := GetCurrentVersion()
|
||||
if config.CurrentVersion == "" || config.CurrentVersion != latestVersion {
|
||||
if config.CurrentVersion != "" {
|
||||
log.Printf("[配置] 配置中的版本号 (%s) 与最新版本号 (%s) 不一致", config.CurrentVersion, latestVersion)
|
||||
}
|
||||
config.CurrentVersion = latestVersion
|
||||
}
|
||||
|
||||
// 使用默认检查地址
|
||||
if config.CheckURL == "" {
|
||||
config.CheckURL = "https://img.1216.top/u-desk/last-version.json"
|
||||
}
|
||||
|
||||
// 确保版本号不为空(使用缓存的版本号)
|
||||
if config.CurrentVersion == "" {
|
||||
config.CurrentVersion = GetCurrentVersion()
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ==================== 常量定义 ====================
|
||||
@@ -15,6 +16,12 @@ import (
|
||||
// AppVersion 应用版本号(发布时直接修改此处)
|
||||
const AppVersion = "0.3.0"
|
||||
|
||||
// 版本号缓存
|
||||
var (
|
||||
cachedVersion string
|
||||
versionOnce sync.Once
|
||||
)
|
||||
|
||||
// ==================== 类型定义 ====================
|
||||
|
||||
// Version 版本号结构
|
||||
@@ -100,22 +107,25 @@ func (v *Version) IsOlderThan(other *Version) bool {
|
||||
|
||||
// ==================== 版本号获取 ====================
|
||||
|
||||
// GetCurrentVersion 获取当前版本号
|
||||
// GetCurrentVersion 获取当前版本号(带缓存)
|
||||
// 优先级:硬编码版本号 > wails.json(开发模式)> 默认值
|
||||
func GetCurrentVersion() string {
|
||||
if AppVersion != "" {
|
||||
log.Printf("[版本] 使用硬编码版本号: %s", AppVersion)
|
||||
return AppVersion
|
||||
}
|
||||
versionOnce.Do(func() {
|
||||
if AppVersion != "" {
|
||||
cachedVersion = AppVersion
|
||||
return
|
||||
}
|
||||
|
||||
version := getVersionFromWailsJSON()
|
||||
if version != "" {
|
||||
log.Printf("[版本] 从 wails.json 获取版本号: %s", version)
|
||||
return version
|
||||
}
|
||||
version := getVersionFromWailsJSON()
|
||||
if version != "" {
|
||||
cachedVersion = version
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[版本] 使用默认版本号: 0.0.1")
|
||||
return "0.0.1"
|
||||
cachedVersion = "0.0.1"
|
||||
})
|
||||
|
||||
return cachedVersion
|
||||
}
|
||||
|
||||
// ==================== 配置文件读取 ====================
|
||||
|
||||
Reference in New Issue
Block a user