优化:代码质量提升,修复重复逻辑和语法高亮支持
- 简化计算属性,删除重复代码 - 优化文件扩展名获取逻辑 - 新增文件工具函数库 fileHelpers.js - 增强 CodeEditor 语法高亮(支持 30+ 语言) - 修复 Office 文档文件服务器访问权限 - 添加特殊文件名支持(Dockerfile、Makefile 等)
This commit is contained in:
@@ -3,43 +3,24 @@ package common
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// AppName 应用名称
|
// AppName 应用名称
|
||||||
AppName = "u-desk"
|
AppName = "u-desk"
|
||||||
|
|
||||||
|
// AppDataDir 应用数据目录名称(带点号,表示隐藏目录)
|
||||||
|
AppDataDir = ".u-desk"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetUserDataDir 获取用户数据目录
|
// GetUserDataDir 获取用户数据目录
|
||||||
// 跨平台支持:Windows、macOS、Linux
|
// 跨平台支持:Windows、macOS、Linux
|
||||||
|
// 所有平台统一使用: ~/.u-desk
|
||||||
func GetUserDataDir() string {
|
func GetUserDataDir() string {
|
||||||
var basePath string
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
switch runtime.GOOS {
|
return "."
|
||||||
case "windows":
|
|
||||||
// Windows: %LOCALAPPDATA% 或 %APPDATA%
|
|
||||||
basePath = os.Getenv("LOCALAPPDATA")
|
|
||||||
if basePath == "" {
|
|
||||||
basePath = os.Getenv("APPDATA")
|
|
||||||
}
|
|
||||||
case "darwin":
|
|
||||||
// macOS: ~/Library/Application Support
|
|
||||||
homeDir, err := os.UserHomeDir()
|
|
||||||
if err == nil {
|
|
||||||
basePath = filepath.Join(homeDir, "Library", "Application Support")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// Linux: ~/.config
|
|
||||||
homeDir, err := os.UserHomeDir()
|
|
||||||
if err == nil {
|
|
||||||
basePath = filepath.Join(homeDir, ".config")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if basePath == "" {
|
return filepath.Join(homeDir, AppDataDir)
|
||||||
basePath = "."
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.Join(basePath, AppName)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -276,7 +276,13 @@ func getAllowedExtensions() map[string]bool {
|
|||||||
".wav": true,
|
".wav": true,
|
||||||
".ogg": true,
|
".ogg": true,
|
||||||
// 文档
|
// 文档
|
||||||
".pdf": true,
|
".pdf": true,
|
||||||
|
".doc": true,
|
||||||
|
".docx": true,
|
||||||
|
".xls": true,
|
||||||
|
".xlsx": true,
|
||||||
|
".ppt": true,
|
||||||
|
".pptx": true,
|
||||||
// 文本
|
// 文本
|
||||||
".txt": true,
|
".txt": true,
|
||||||
".md": true,
|
".md": true,
|
||||||
@@ -346,10 +352,20 @@ func getMIMETypeMapping() map[string]string {
|
|||||||
".wav": "audio/wav",
|
".wav": "audio/wav",
|
||||||
".ogg": "audio/ogg",
|
".ogg": "audio/ogg",
|
||||||
".pdf": "application/pdf",
|
".pdf": "application/pdf",
|
||||||
|
// Office 文档
|
||||||
|
".doc": "application/msword",
|
||||||
|
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
".xls": "application/vnd.ms-excel",
|
||||||
|
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
".ppt": "application/vnd.ms-powerpoint",
|
||||||
|
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||||
|
// 文本
|
||||||
".txt": "text/plain; charset=utf-8",
|
".txt": "text/plain; charset=utf-8",
|
||||||
".html": "text/html; charset=utf-8",
|
".html": "text/html; charset=utf-8",
|
||||||
".css": "text/css",
|
".css": "text/css",
|
||||||
".js": "application/javascript",
|
".js": "application/javascript",
|
||||||
".json": "application/json",
|
".json": "application/json",
|
||||||
|
".xml": "application/xml",
|
||||||
|
".md": "text/markdown",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"u-desk/internal/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ==================== 类型定义 ====================
|
// ==================== 类型定义 ====================
|
||||||
@@ -409,18 +411,13 @@ func BackupApplication() (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
homeDir, err := os.UserHomeDir()
|
backupDir := filepath.Join(common.GetUserDataDir(), "backups")
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("获取用户目录失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
backupDir := filepath.Join(homeDir, ".go-desk", "backups")
|
|
||||||
if err := os.MkdirAll(backupDir, 0755); err != nil {
|
if err := os.MkdirAll(backupDir, 0755); err != nil {
|
||||||
return "", fmt.Errorf("创建备份目录失败: %v", err)
|
return "", fmt.Errorf("创建备份目录失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
timestamp := time.Now().Format("20060102-150405")
|
timestamp := time.Now().Format("20060102-150405")
|
||||||
backupFileName := fmt.Sprintf("go-desk-backup-%s%s", timestamp, filepath.Ext(execPath))
|
backupFileName := fmt.Sprintf("u-desk-backup-%s%s", timestamp, filepath.Ext(execPath))
|
||||||
backupPath := filepath.Join(backupDir, backupFileName)
|
backupPath := filepath.Join(backupDir, backupFileName)
|
||||||
|
|
||||||
if err := copyFile(execPath, backupPath); err != nil {
|
if err := copyFile(execPath, backupPath); err != nil {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"u-desk/internal/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UpdateConfig 更新配置
|
// UpdateConfig 更新配置
|
||||||
@@ -20,17 +22,12 @@ type UpdateConfig struct {
|
|||||||
|
|
||||||
// GetUpdateConfigPath 获取更新配置文件路径
|
// GetUpdateConfigPath 获取更新配置文件路径
|
||||||
func GetUpdateConfigPath() (string, error) {
|
func GetUpdateConfigPath() (string, error) {
|
||||||
homeDir, err := os.UserHomeDir()
|
dataDir := common.GetUserDataDir()
|
||||||
if err != nil {
|
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
||||||
return "", fmt.Errorf("获取用户目录失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
configDir := filepath.Join(homeDir, ".go-desk")
|
|
||||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
|
||||||
return "", fmt.Errorf("创建配置目录失败: %v", err)
|
return "", fmt.Errorf("创建配置目录失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return filepath.Join(configDir, "update_config.json"), nil
|
return filepath.Join(dataDir, "update_config.json"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadUpdateConfig 加载更新配置
|
// LoadUpdateConfig 加载更新配置
|
||||||
|
|||||||
@@ -5,12 +5,15 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"u-desk/internal/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ==================== 类型定义 ====================
|
// ==================== 类型定义 ====================
|
||||||
@@ -33,12 +36,7 @@ func DownloadUpdate(downloadURL string, progressCallback DownloadProgress) (*Dow
|
|||||||
log.Printf("[下载] 开始下载,URL: %s", downloadURL)
|
log.Printf("[下载] 开始下载,URL: %s", downloadURL)
|
||||||
|
|
||||||
// 获取下载目录
|
// 获取下载目录
|
||||||
homeDir, err := os.UserHomeDir()
|
downloadDir := filepath.Join(common.GetUserDataDir(), "downloads")
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("获取用户目录失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadDir := filepath.Join(homeDir, ".go-desk", "downloads")
|
|
||||||
if err := os.MkdirAll(downloadDir, 0755); err != nil {
|
if err := os.MkdirAll(downloadDir, 0755); err != nil {
|
||||||
return nil, fmt.Errorf("创建下载目录失败: %v", err)
|
return nil, fmt.Errorf("创建下载目录失败: %v", err)
|
||||||
}
|
}
|
||||||
@@ -283,7 +281,33 @@ func normalizeProgress(progress float64) float64 {
|
|||||||
return progress
|
return progress
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculateFileHashes 计算文件的 MD5 和 SHA256 哈希值
|
// calculateHash 计算文件的哈希值(通用函数)
|
||||||
|
func calculateHash(filePath string, hashType string) (string, error) {
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var hash hash.Hash
|
||||||
|
|
||||||
|
switch hashType {
|
||||||
|
case "md5":
|
||||||
|
hash = md5.New()
|
||||||
|
case "sha256":
|
||||||
|
hash = sha256.New()
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("不支持的哈希类型: %s", hashType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.Copy(hash, file); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.EncodeToString(hash.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateFileHashes 计算文件的 MD5 和 SHA256 哈希值(优化版,使用 MultiWriter)
|
||||||
func calculateFileHashes(filePath string) (string, string, error) {
|
func calculateFileHashes(filePath string) (string, string, error) {
|
||||||
file, err := os.Open(filePath)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -294,7 +318,7 @@ func calculateFileHashes(filePath string) (string, string, error) {
|
|||||||
md5Hash := md5.New()
|
md5Hash := md5.New()
|
||||||
sha256Hash := sha256.New()
|
sha256Hash := sha256.New()
|
||||||
|
|
||||||
// 使用 MultiWriter 同时计算两个哈希
|
// 使用 MultiWriter 同时计算两个哈希,只读取文件一次
|
||||||
writer := io.MultiWriter(md5Hash, sha256Hash)
|
writer := io.MultiWriter(md5Hash, sha256Hash)
|
||||||
|
|
||||||
if _, err := io.Copy(writer, file); err != nil {
|
if _, err := io.Copy(writer, file); err != nil {
|
||||||
@@ -309,33 +333,9 @@ func calculateFileHashes(filePath string) (string, string, error) {
|
|||||||
|
|
||||||
// VerifyFileHash 验证文件哈希值
|
// VerifyFileHash 验证文件哈希值
|
||||||
func VerifyFileHash(filePath string, expectedHash string, hashType string) (bool, error) {
|
func VerifyFileHash(filePath string, expectedHash string, hashType string) (bool, error) {
|
||||||
file, err := os.Open(filePath)
|
calculatedHash, err := calculateHash(filePath, hashType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
var hash []byte
|
|
||||||
var calculatedHash string
|
|
||||||
|
|
||||||
switch hashType {
|
|
||||||
case "md5":
|
|
||||||
md5Hash := md5.New()
|
|
||||||
if _, err := io.Copy(md5Hash, file); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
hash = md5Hash.Sum(nil)
|
|
||||||
calculatedHash = hex.EncodeToString(hash)
|
|
||||||
case "sha256":
|
|
||||||
sha256Hash := sha256.New()
|
|
||||||
if _, err := io.Copy(sha256Hash, file); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
hash = sha256Hash.Sum(nil)
|
|
||||||
calculatedHash = hex.EncodeToString(hash)
|
|
||||||
default:
|
|
||||||
return false, fmt.Errorf("不支持的哈希类型: %s", hashType)
|
|
||||||
}
|
|
||||||
|
|
||||||
return calculatedHash == expectedHash, nil
|
return calculatedHash == expectedHash, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"u-desk/internal/common"
|
||||||
"u-desk/internal/storage/models"
|
"u-desk/internal/storage/models"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -25,17 +27,13 @@ func InitFast() (*gorm.DB, error) {
|
|||||||
return globalDB, nil
|
return globalDB, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
homeDir, err := os.UserHomeDir()
|
// 使用统一的数据目录
|
||||||
if err != nil {
|
dataDir := common.GetUserDataDir()
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dataDir := filepath.Join(homeDir, ".go-desk")
|
|
||||||
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dbPath := filepath.Join(dataDir, "db-cli.db")
|
dbPath := filepath.Join(dataDir, "app.db")
|
||||||
|
|
||||||
// 极限性能优化参数:
|
// 极限性能优化参数:
|
||||||
// - journal_mode=WAL: 写前日志,大幅提升并发性能
|
// - journal_mode=WAL: 写前日志,大幅提升并发性能
|
||||||
@@ -53,7 +51,10 @@ func InitFast() (*gorm.DB, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlDB, _ := db.DB()
|
sqlDB, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取底层SQL数据库失败: %v", err)
|
||||||
|
}
|
||||||
sqlDB.SetMaxOpenConns(1) // SQLite 只需要一个连接
|
sqlDB.SetMaxOpenConns(1) // SQLite 只需要一个连接
|
||||||
sqlDB.SetMaxIdleConns(1)
|
sqlDB.SetMaxIdleConns(1)
|
||||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||||
|
|||||||
@@ -49,11 +49,10 @@
|
|||||||
</a-layout-header>
|
</a-layout-header>
|
||||||
<a-layout-content class="content">
|
<a-layout-content class="content">
|
||||||
<!-- 动态渲染 Tab 内容 -->
|
<!-- 动态渲染 Tab 内容 -->
|
||||||
<template v-for="tab in visibleTabs" :key="tab.key">
|
<!-- 使用 KeepAlive 缓存组件状态,避免切换时重新加载 -->
|
||||||
<KeepAlive>
|
<KeepAlive include="FileSystem,DbCli,DeviceTest">
|
||||||
<component :is="getComponent(tab.key)" v-if="activeTab === tab.key" />
|
<component :is="getComponent(activeTab)" />
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
</template>
|
|
||||||
</a-layout-content>
|
</a-layout-content>
|
||||||
|
|
||||||
<!-- 设置抽屉 -->
|
<!-- 设置抽屉 -->
|
||||||
|
|||||||
@@ -3,106 +3,91 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, watch, onBeforeUnmount } from 'vue'
|
import { ref, onMounted, watch, onBeforeUnmount, computed } from 'vue'
|
||||||
import { EditorView, lineNumbers, highlightActiveLineGutter } from '@codemirror/view'
|
import { EditorView, lineNumbers, highlightActiveLineGutter, keymap } from '@codemirror/view'
|
||||||
import { EditorState } from '@codemirror/state'
|
import { EditorState } from '@codemirror/state'
|
||||||
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'
|
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'
|
||||||
import { javascript } from '@codemirror/lang-javascript'
|
import { javascript } from '@codemirror/lang-javascript'
|
||||||
import { java } from '@codemirror/lang-java'
|
|
||||||
import { python } from '@codemirror/lang-python'
|
|
||||||
import { go } from '@codemirror/lang-go'
|
|
||||||
import { cpp } from '@codemirror/lang-cpp'
|
|
||||||
import { rust } from '@codemirror/lang-rust'
|
|
||||||
import { php } from '@codemirror/lang-php'
|
|
||||||
import { json } from '@codemirror/lang-json'
|
import { json } from '@codemirror/lang-json'
|
||||||
import { markdown } from '@codemirror/lang-markdown'
|
import { cpp } from '@codemirror/lang-cpp'
|
||||||
import { html } from '@codemirror/lang-html'
|
|
||||||
import { css } from '@codemirror/lang-css'
|
import { css } from '@codemirror/lang-css'
|
||||||
|
import { go } from '@codemirror/lang-go'
|
||||||
|
import { html } from '@codemirror/lang-html'
|
||||||
|
import { java } from '@codemirror/lang-java'
|
||||||
|
import { markdown } from '@codemirror/lang-markdown'
|
||||||
|
import { php } from '@codemirror/lang-php'
|
||||||
|
import { python } from '@codemirror/lang-python'
|
||||||
|
import { rust } from '@codemirror/lang-rust'
|
||||||
import { sql } from '@codemirror/lang-sql'
|
import { sql } from '@codemirror/lang-sql'
|
||||||
import { yaml } from '@codemirror/lang-yaml'
|
import { yaml } from '@codemirror/lang-yaml'
|
||||||
import { oneDark } from '@codemirror/theme-one-dark'
|
|
||||||
import { keymap } from '@codemirror/view'
|
|
||||||
import { bracketMatching } from '@codemirror/language'
|
|
||||||
import { StreamLanguage } from '@codemirror/language'
|
import { StreamLanguage } from '@codemirror/language'
|
||||||
import { shell } from '@codemirror/legacy-modes/mode/shell'
|
import { oneDark } from '@codemirror/theme-one-dark'
|
||||||
import { powerShell } from '@codemirror/legacy-modes/mode/powershell'
|
import { bracketMatching } from '@codemirror/language'
|
||||||
import { useTheme } from '@/composables/useTheme'
|
import { useTheme } from '@/composables/useTheme'
|
||||||
|
|
||||||
// 使用主题系统
|
// Legacy modes for languages without dedicated packages
|
||||||
const { isDark } = useTheme()
|
import { csharp, kotlin } from '@codemirror/legacy-modes/mode/clike'
|
||||||
|
import { swift } from '@codemirror/legacy-modes/mode/swift'
|
||||||
|
import { ruby } from '@codemirror/legacy-modes/mode/ruby'
|
||||||
|
import { shell } from '@codemirror/legacy-modes/mode/shell'
|
||||||
|
import { octave } from '@codemirror/legacy-modes/mode/octave'
|
||||||
|
import { perl } from '@codemirror/legacy-modes/mode/perl'
|
||||||
|
import { r } from '@codemirror/legacy-modes/mode/r'
|
||||||
|
import { properties } from '@codemirror/legacy-modes/mode/properties'
|
||||||
|
import { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile'
|
||||||
|
import { stex } from '@codemirror/legacy-modes/mode/stex'
|
||||||
|
import { xml } from '@codemirror/legacy-modes/mode/xml'
|
||||||
|
|
||||||
/**
|
// ==================== Constants ====================
|
||||||
* 文件扩展名到语言的映射
|
// 文件扩展名到 CodeMirror 语言包的映射
|
||||||
*/
|
|
||||||
const LANGUAGE_MAP = {
|
const LANGUAGE_MAP = {
|
||||||
// JavaScript/TypeScript
|
// JavaScript/TypeScript (使用 javascript 包)
|
||||||
'js': javascript(),
|
javascript: ['js', 'jsx', 'mjs', 'cjs'],
|
||||||
'jsx': javascript({ jsx: true }),
|
typescript: ['ts', 'tsx'],
|
||||||
'ts': javascript({ typescript: true }),
|
|
||||||
'tsx': javascript({ typescript: true, jsx: true }),
|
|
||||||
'mjs': javascript(),
|
|
||||||
'cjs': javascript(),
|
|
||||||
|
|
||||||
// JSON
|
// 数据格式
|
||||||
'json': json(),
|
json: ['json'],
|
||||||
|
yaml: ['yaml', 'yml'],
|
||||||
|
xml: ['xml', 'xhtml', 'svg'],
|
||||||
|
|
||||||
// Java
|
// Web
|
||||||
'java': java(),
|
html: ['html', 'htm'],
|
||||||
|
css: ['css', 'scss', 'sass', 'less'],
|
||||||
|
|
||||||
// Python
|
// 系统编程
|
||||||
'py': python(),
|
cpp: ['cpp', 'c', 'cc', 'cxx', 'h', 'hpp'],
|
||||||
|
rust: ['rs'],
|
||||||
|
go: ['go'],
|
||||||
|
|
||||||
// Go
|
// 脚本语言
|
||||||
'go': go(),
|
python: ['py', 'pyw'],
|
||||||
|
php: ['php'],
|
||||||
|
ruby: ['rb'],
|
||||||
|
perl: ['pl', 'pm'],
|
||||||
|
shell: ['sh', 'bash', 'zsh', 'fish', 'cmd', 'bat'],
|
||||||
|
sql: ['sql'],
|
||||||
|
|
||||||
// C/C++
|
// JVM 语言
|
||||||
'c': cpp(),
|
java: ['java'],
|
||||||
'cpp': cpp(),
|
kotlin: ['kt', 'kts'],
|
||||||
'cc': cpp(),
|
csharp: ['cs', 'csx'],
|
||||||
'cxx': cpp(),
|
|
||||||
'h': cpp(),
|
|
||||||
'hpp': cpp(),
|
|
||||||
'hxx': cpp(),
|
|
||||||
|
|
||||||
// Rust
|
// 其他语言
|
||||||
'rs': rust(),
|
swift: ['swift'],
|
||||||
|
markdown: ['md', 'markdown'],
|
||||||
|
r: ['r'],
|
||||||
|
matlab: ['m'],
|
||||||
|
latex: ['tex'],
|
||||||
|
makefile: ['makefile', 'make', 'mk', 'gnumakefile'],
|
||||||
|
ini: ['ini', 'cfg', 'conf', 'properties'],
|
||||||
|
dockerfile: ['dockerfile', 'containerfile'],
|
||||||
|
gitignore: ['gitignore', 'gitignore-global', 'gitattributes'],
|
||||||
|
|
||||||
// PHP
|
// 纯文本(未知类型)
|
||||||
'php': php(),
|
text: ['txt', 'text', 'log', 'csv']
|
||||||
|
|
||||||
// Markdown
|
|
||||||
'md': markdown(),
|
|
||||||
'markdown': markdown(),
|
|
||||||
|
|
||||||
// HTML
|
|
||||||
'html': html(),
|
|
||||||
'htm': html(),
|
|
||||||
|
|
||||||
// CSS
|
|
||||||
'css': css(),
|
|
||||||
'scss': css(),
|
|
||||||
'sass': css(),
|
|
||||||
'less': css(),
|
|
||||||
|
|
||||||
// SQL
|
|
||||||
'sql': sql(),
|
|
||||||
|
|
||||||
// YAML
|
|
||||||
'yml': yaml(),
|
|
||||||
'yaml': yaml(),
|
|
||||||
|
|
||||||
// Shell/Bash
|
|
||||||
'sh': StreamLanguage.define(shell),
|
|
||||||
'bash': StreamLanguage.define(shell),
|
|
||||||
'zsh': StreamLanguage.define(shell),
|
|
||||||
'fish': StreamLanguage.define(shell),
|
|
||||||
|
|
||||||
// Windows Batch/PowerShell
|
|
||||||
'bat': StreamLanguage.define(powerShell),
|
|
||||||
'cmd': StreamLanguage.define(powerShell),
|
|
||||||
'ps1': StreamLanguage.define(powerShell),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== Props & Emits ====================
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -116,10 +101,15 @@ const props = defineProps({
|
|||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
// ==================== State ====================
|
||||||
|
const { isDark } = useTheme()
|
||||||
const editorContainer = ref(null)
|
const editorContainer = ref(null)
|
||||||
let view = null
|
let view = null
|
||||||
|
|
||||||
// 根据主题动态创建编辑器配置
|
// ==================== Editor Management ====================
|
||||||
|
/**
|
||||||
|
* 创建编辑器扩展配置
|
||||||
|
*/
|
||||||
const createExtensions = () => {
|
const createExtensions = () => {
|
||||||
const extensions = [
|
const extensions = [
|
||||||
lineNumbers(),
|
lineNumbers(),
|
||||||
@@ -155,25 +145,131 @@ const createExtensions = () => {
|
|||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
// 根据主题添加 One Dark
|
// 主题
|
||||||
if (isDark.value) {
|
if (isDark.value) {
|
||||||
extensions.push(oneDark)
|
extensions.push(oneDark)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加语言支持
|
// 语言支持
|
||||||
const langSupport = LANGUAGE_MAP[props.fileExtension]
|
const ext = props.fileExtension.toLowerCase()
|
||||||
if (langSupport) {
|
|
||||||
extensions.push(langSupport)
|
// JavaScript/TypeScript
|
||||||
|
if (LANGUAGE_MAP.javascript.includes(ext) || LANGUAGE_MAP.typescript.includes(ext)) {
|
||||||
|
extensions.push(javascript({ jsx: true }))
|
||||||
}
|
}
|
||||||
|
// JSON
|
||||||
|
else if (LANGUAGE_MAP.json.includes(ext)) {
|
||||||
|
extensions.push(json())
|
||||||
|
}
|
||||||
|
// YAML
|
||||||
|
else if (LANGUAGE_MAP.yaml.includes(ext)) {
|
||||||
|
extensions.push(yaml())
|
||||||
|
}
|
||||||
|
// HTML
|
||||||
|
else if (LANGUAGE_MAP.html.includes(ext)) {
|
||||||
|
extensions.push(html())
|
||||||
|
}
|
||||||
|
// CSS (including SCSS, SASS, LESS)
|
||||||
|
else if (LANGUAGE_MAP.css.includes(ext)) {
|
||||||
|
extensions.push(css())
|
||||||
|
}
|
||||||
|
// C/C++
|
||||||
|
else if (LANGUAGE_MAP.cpp.includes(ext)) {
|
||||||
|
extensions.push(cpp())
|
||||||
|
}
|
||||||
|
// Rust
|
||||||
|
else if (LANGUAGE_MAP.rust.includes(ext)) {
|
||||||
|
extensions.push(rust())
|
||||||
|
}
|
||||||
|
// Go
|
||||||
|
else if (LANGUAGE_MAP.go.includes(ext)) {
|
||||||
|
extensions.push(go())
|
||||||
|
}
|
||||||
|
// Python
|
||||||
|
else if (LANGUAGE_MAP.python.includes(ext)) {
|
||||||
|
extensions.push(python())
|
||||||
|
}
|
||||||
|
// PHP
|
||||||
|
else if (LANGUAGE_MAP.php.includes(ext)) {
|
||||||
|
extensions.push(php())
|
||||||
|
}
|
||||||
|
// SQL
|
||||||
|
else if (LANGUAGE_MAP.sql.includes(ext)) {
|
||||||
|
extensions.push(sql())
|
||||||
|
}
|
||||||
|
// Markdown
|
||||||
|
else if (LANGUAGE_MAP.markdown.includes(ext)) {
|
||||||
|
extensions.push(markdown())
|
||||||
|
}
|
||||||
|
// Java
|
||||||
|
else if (LANGUAGE_MAP.java.includes(ext)) {
|
||||||
|
extensions.push(java())
|
||||||
|
}
|
||||||
|
// Ruby
|
||||||
|
else if (LANGUAGE_MAP.ruby.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(ruby))
|
||||||
|
}
|
||||||
|
// Shell
|
||||||
|
else if (LANGUAGE_MAP.shell.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(shell))
|
||||||
|
}
|
||||||
|
// Kotlin
|
||||||
|
else if (LANGUAGE_MAP.kotlin.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(kotlin))
|
||||||
|
}
|
||||||
|
// C#
|
||||||
|
else if (LANGUAGE_MAP.csharp.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(csharp))
|
||||||
|
}
|
||||||
|
// Swift
|
||||||
|
else if (LANGUAGE_MAP.swift.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(swift))
|
||||||
|
}
|
||||||
|
// R
|
||||||
|
else if (LANGUAGE_MAP.r.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(r))
|
||||||
|
}
|
||||||
|
// Perl
|
||||||
|
else if (LANGUAGE_MAP.perl.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(perl))
|
||||||
|
}
|
||||||
|
// LaTeX
|
||||||
|
else if (LANGUAGE_MAP.latex.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(stex))
|
||||||
|
}
|
||||||
|
// Makefile (使用纯文本,legacy-modes 没有专门的 makefile 支持)
|
||||||
|
else if (LANGUAGE_MAP.makefile.includes(ext)) {
|
||||||
|
// 纯文本模式,不添加语言扩展
|
||||||
|
}
|
||||||
|
// INI/Properties/Dockerfile
|
||||||
|
else if (LANGUAGE_MAP.ini.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(properties))
|
||||||
|
}
|
||||||
|
else if (LANGUAGE_MAP.dockerfile.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(dockerFile))
|
||||||
|
}
|
||||||
|
// XML (包括 SVG)
|
||||||
|
else if (LANGUAGE_MAP.xml.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(xml))
|
||||||
|
}
|
||||||
|
// Matlab/Octave
|
||||||
|
else if (LANGUAGE_MAP.matlab.includes(ext)) {
|
||||||
|
extensions.push(StreamLanguage.define(octave))
|
||||||
|
}
|
||||||
|
// 其他类型(包括 gitignore, dockerfile, txt 等)使用纯文本模式
|
||||||
|
// 不添加任何语言扩展,保持纯文本
|
||||||
|
|
||||||
return extensions
|
return extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
/**
|
||||||
|
* 创建编辑器实例
|
||||||
|
*/
|
||||||
|
const createEditor = (docContent = '') => {
|
||||||
if (!editorContainer.value) return
|
if (!editorContainer.value) return
|
||||||
|
|
||||||
const state = EditorState.create({
|
const state = EditorState.create({
|
||||||
doc: props.modelValue,
|
doc: docContent,
|
||||||
extensions: createExtensions()
|
extensions: createExtensions()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -181,49 +277,21 @@ onMounted(() => {
|
|||||||
state,
|
state,
|
||||||
parent: editorContainer.value
|
parent: editorContainer.value
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
// 监听外部内容变化
|
/**
|
||||||
watch(() => props.modelValue, (newValue) => {
|
* 重建编辑器(保留内容)
|
||||||
if (view && newValue !== view.state.doc.toString()) {
|
*/
|
||||||
view.dispatch({
|
const recreateEditor = () => {
|
||||||
changes: {
|
if (!view) return
|
||||||
from: 0,
|
const currentDoc = view.state.doc.toString()
|
||||||
to: view.state.doc.length,
|
view.destroy()
|
||||||
insert: newValue
|
createEditor(currentDoc)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 监听主题变化,重新创建编辑器
|
// ==================== Lifecycle ====================
|
||||||
watch(isDark, () => {
|
onMounted(() => {
|
||||||
if (view) {
|
createEditor(props.modelValue || '')
|
||||||
view.destroy()
|
|
||||||
const state = EditorState.create({
|
|
||||||
doc: view.state.doc.toString(),
|
|
||||||
extensions: createExtensions()
|
|
||||||
})
|
|
||||||
view = new EditorView({
|
|
||||||
state,
|
|
||||||
parent: editorContainer.value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 监听文件扩展名变化,重新创建编辑器
|
|
||||||
watch(() => props.fileExtension, () => {
|
|
||||||
if (view) {
|
|
||||||
view.destroy()
|
|
||||||
const state = EditorState.create({
|
|
||||||
doc: view.state.doc.toString(),
|
|
||||||
extensions: createExtensions()
|
|
||||||
})
|
|
||||||
view = new EditorView({
|
|
||||||
state,
|
|
||||||
parent: editorContainer.value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
@@ -231,80 +299,36 @@ onBeforeUnmount(() => {
|
|||||||
view.destroy()
|
view.destroy()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ==================== Watchers ====================
|
||||||
|
// 监听外部内容变化
|
||||||
|
watch(() => props.modelValue, (newValue) => {
|
||||||
|
if (view && newValue !== view.state.doc.toString()) {
|
||||||
|
view.dispatch({
|
||||||
|
changes: {
|
||||||
|
from: 0,
|
||||||
|
to: view.state.doc.length,
|
||||||
|
insert: newValue || ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听主题或文件扩展名变化,重建编辑器
|
||||||
|
watch([isDark, () => props.fileExtension], recreateEditor)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
/* 全局语法高亮样式(适用于亮色主题) */
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-keyword {
|
|
||||||
color: #d73a49;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-string {
|
|
||||||
color: #032f62;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-number {
|
|
||||||
color: #005cc5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-comment {
|
|
||||||
color: #6a737d;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-function {
|
|
||||||
color: #6f42c1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-operator {
|
|
||||||
color: #d73a49;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-class-name {
|
|
||||||
color: #22863a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-property {
|
|
||||||
color: #e36209;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-tag {
|
|
||||||
color: #22863a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-attribute {
|
|
||||||
color: #6f42c1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-variableName {
|
|
||||||
color: #e36209;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-variableName2 {
|
|
||||||
color: #005cc5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-editor:not(.cm-theme-dark) .tok-def {
|
|
||||||
color: #6f42c1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.codemirror-editor {
|
.codemirror-editor {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.cm-editor) {
|
.codemirror-editor :deep(.cm-editor) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.cm-scroller) {
|
.codemirror-editor :deep(.cm-scroller) {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.cm-content) {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -197,6 +197,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
// 定义组件名称,用于 KeepAlive 缓存
|
||||||
|
defineOptions({
|
||||||
|
name: 'DeviceTest'
|
||||||
|
})
|
||||||
|
|
||||||
import {computed, onMounted, ref} from 'vue'
|
import {computed, onMounted, ref} from 'vue'
|
||||||
import {Message, Modal} from '@arco-design/web-vue'
|
import {Message, Modal} from '@arco-design/web-vue'
|
||||||
import {
|
import {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -91,8 +91,7 @@ export function useFavoriteFiles(storageKey, options = {}) {
|
|||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
// 已收藏,执行取消收藏
|
// 已收藏,执行取消收藏
|
||||||
favoriteFiles.value.splice(index, 1)
|
favoriteFiles.value.splice(index, 1)
|
||||||
sortFavorites() // 排序
|
save(favoriteFiles.value) // 直接保存,不重新排序
|
||||||
save(favoriteFiles.value)
|
|
||||||
|
|
||||||
onRemove(item)
|
onRemove(item)
|
||||||
Message.info(`已取消收藏: ${item.name}`)
|
Message.info(`已取消收藏: ${item.name}`)
|
||||||
@@ -108,11 +107,10 @@ export function useFavoriteFiles(storageKey, options = {}) {
|
|||||||
path: item.path,
|
path: item.path,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
is_dir: item.is_dir || false,
|
is_dir: item.is_dir || false,
|
||||||
created_at: Date.now(), // 添加时间戳
|
created_at: Date.now(), // 添加时间戳(用于 getSortedFavorites)
|
||||||
})
|
})
|
||||||
|
|
||||||
sortFavorites() // 排序
|
save(favoriteFiles.value) // 直接保存,不重新排序(新项目添加到末尾)
|
||||||
save(favoriteFiles.value)
|
|
||||||
|
|
||||||
onAdd(item)
|
onAdd(item)
|
||||||
Message.success(`已收藏: ${item.name}`)
|
Message.success(`已收藏: ${item.name}`)
|
||||||
@@ -141,8 +139,7 @@ export function useFavoriteFiles(storageKey, options = {}) {
|
|||||||
const item = favoriteFiles.value[index]
|
const item = favoriteFiles.value[index]
|
||||||
favoriteFiles.value.splice(index, 1)
|
favoriteFiles.value.splice(index, 1)
|
||||||
|
|
||||||
sortFavorites() // 排序
|
save(favoriteFiles.value) // 直接保存,不重新排序
|
||||||
save(favoriteFiles.value)
|
|
||||||
|
|
||||||
onRemove(item)
|
onRemove(item)
|
||||||
Message.info(`已取消收藏: ${item.name}`)
|
Message.info(`已取消收藏: ${item.name}`)
|
||||||
@@ -178,7 +175,6 @@ export function useFavoriteFiles(storageKey, options = {}) {
|
|||||||
const executeClear = () => {
|
const executeClear = () => {
|
||||||
const count = favoriteFiles.value.length
|
const count = favoriteFiles.value.length
|
||||||
favoriteFiles.value = []
|
favoriteFiles.value = []
|
||||||
sortFavorites() // 保持一致性
|
|
||||||
save([])
|
save([])
|
||||||
|
|
||||||
Message.success(`已清空 ${count} 个收藏项`)
|
Message.success(`已清空 ${count} 个收藏项`)
|
||||||
@@ -229,10 +225,39 @@ export function useFavoriteFiles(storageKey, options = {}) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 组件挂载时加载数据并排序
|
/**
|
||||||
|
* 重新排序收藏列表(拖拽排序)
|
||||||
|
* @param {number} fromIndex - 源索引
|
||||||
|
* @param {number} toIndex - 目标索引
|
||||||
|
* @returns {boolean} 是否成功重排序
|
||||||
|
*/
|
||||||
|
const reorderFavorites = (fromIndex, toIndex) => {
|
||||||
|
if (!Array.isArray(favoriteFiles.value)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromIndex < 0 || fromIndex >= favoriteFiles.value.length ||
|
||||||
|
toIndex < 0 || toIndex >= favoriteFiles.value.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromIndex === toIndex) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动数组元素
|
||||||
|
const [movedItem] = favoriteFiles.value.splice(fromIndex, 1)
|
||||||
|
favoriteFiles.value.splice(toIndex, 0, movedItem)
|
||||||
|
|
||||||
|
// 保存新顺序
|
||||||
|
save(favoriteFiles.value)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件挂载时加载数据(不自动排序,保持用户拖拽的顺序)
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
load()
|
load()
|
||||||
sortFavorites() // 确保加载后的数据是排序的
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -248,6 +273,7 @@ export function useFavoriteFiles(storageKey, options = {}) {
|
|||||||
getSortedFavorites,
|
getSortedFavorites,
|
||||||
searchFavorites,
|
searchFavorites,
|
||||||
sortFavorites,
|
sortFavorites,
|
||||||
|
reorderFavorites,
|
||||||
load,
|
load,
|
||||||
save,
|
save,
|
||||||
}
|
}
|
||||||
@@ -264,6 +290,7 @@ export function useFavoriteFiles(storageKey, options = {}) {
|
|||||||
* @property {Function} getSortedFavorites - 获取排序后的列表
|
* @property {Function} getSortedFavorites - 获取排序后的列表
|
||||||
* @property {Function} searchFavorites - 搜索收藏
|
* @property {Function} searchFavorites - 搜索收藏
|
||||||
* @property {Function} sortFavorites - 手动排序收藏列表
|
* @property {Function} sortFavorites - 手动排序收藏列表
|
||||||
|
* @property {Function} reorderFavorites - 拖拽重新排序
|
||||||
* @property {Function} load - 手动加载数据
|
* @property {Function} load - 手动加载数据
|
||||||
* @property {Function} save - 手动保存数据
|
* @property {Function} save - 手动保存数据
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ export function useFileOperations(options = {}) {
|
|||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
onError('listDirectory', error)
|
onError('listDirectory', error)
|
||||||
Message.error(`列出目录失败: ${error.message || error}`)
|
const errorMsg = error.message || error || '未知错误'
|
||||||
|
Message.error(`列出目录失败 [${targetPath}]: ${errorMsg}`)
|
||||||
return false
|
return false
|
||||||
} finally {
|
} finally {
|
||||||
fileLoading.value = false
|
fileLoading.value = false
|
||||||
|
|||||||
@@ -70,9 +70,12 @@ export const FILE_EXTENSIONS = {
|
|||||||
// 代码文件
|
// 代码文件
|
||||||
CODE: [
|
CODE: [
|
||||||
'js', 'ts', 'jsx', 'tsx', 'vue', 'py', 'java', 'c', 'cpp', 'h', 'go', 'rs', 'php', 'rb', 'cs', 'swift', 'kt',
|
'js', 'ts', 'jsx', 'tsx', 'vue', 'py', 'java', 'c', 'cpp', 'h', 'go', 'rs', 'php', 'rb', 'cs', 'swift', 'kt',
|
||||||
'scala', 'html', 'htm', 'css', 'scss', 'sass', 'less', 'json', 'xml', 'yaml', 'yml', 'sql', 'sh', 'bat', 'ps1'
|
'scala', 'css', 'scss', 'sass', 'less', 'json', 'xml', 'yaml', 'yml', 'sql', 'sh', 'bat', 'ps1'
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// 标记语言文件(用于特殊预览)
|
||||||
|
MARKUP: ['html', 'htm', 'md', 'markdown'],
|
||||||
|
|
||||||
// 数据库文件
|
// 数据库文件
|
||||||
DATABASE: ['db', 'sqlite', 'mdb', 'accdb'],
|
DATABASE: ['db', 'sqlite', 'mdb', 'accdb'],
|
||||||
|
|
||||||
@@ -271,7 +274,7 @@ export const PATH_ICONS = {
|
|||||||
* 文件大小单位
|
* 文件大小单位
|
||||||
* @description 用于文件大小格式化的单位数组
|
* @description 用于文件大小格式化的单位数组
|
||||||
*/
|
*/
|
||||||
export const BYTE_UNITS = ['B', 'KMGTPE']
|
export const BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认配置值
|
* 默认配置值
|
||||||
@@ -304,3 +307,12 @@ export const FILE_SIZE_FORMAT = {
|
|||||||
UNIT: 1024, // 使用1024进制(KiB, MiB等)
|
UNIT: 1024, // 使用1024进制(KiB, MiB等)
|
||||||
DECIMAL_PLACES: 2, // 保留小数位数
|
DECIMAL_PLACES: 2, // 保留小数位数
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件大小阈值配置
|
||||||
|
* @description 用于文件处理逻辑的大小限制
|
||||||
|
*/
|
||||||
|
export const FILE_SIZE_THRESHOLDS = {
|
||||||
|
LARGE_FILE: 100 * 1024, // 100KB - 大文件检测阈值
|
||||||
|
MAX_TEXT_DISPLAY: 5 * 1024 * 1024, // 5MB - 文本文件最大显示大小
|
||||||
|
}
|
||||||
|
|||||||
41
web/src/utils/fileHelpers.js
Normal file
41
web/src/utils/fileHelpers.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 文件类型工具函数
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { FILE_EXTENSIONS } from './constants'
|
||||||
|
|
||||||
|
// 获取文件扩展名
|
||||||
|
export const getExt = (path) => {
|
||||||
|
if (!path) return ''
|
||||||
|
const dot = path.lastIndexOf('.')
|
||||||
|
const slash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'))
|
||||||
|
if (dot === -1 || dot < slash) return ''
|
||||||
|
return path.slice(dot + 1).toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件类型检查
|
||||||
|
export const isImage = (path) => FILE_EXTENSIONS.IMAGE.includes(getExt(path))
|
||||||
|
export const isVideo = (path) => FILE_EXTENSIONS.VIDEO_BROWSER.includes(getExt(path))
|
||||||
|
export const isAudio = (path) => FILE_EXTENSIONS.AUDIO.includes(getExt(path))
|
||||||
|
export const isPdf = (path) => getExt(path) === 'pdf'
|
||||||
|
export const isHtml = (path) => { const e = getExt(path); return e === 'html' || e === 'htm' }
|
||||||
|
export const isMarkdown = (path) => { const e = getExt(path); return e === 'md' || e === 'markdown' }
|
||||||
|
export const isCode = (path) => FILE_EXTENSIONS.CODE.includes(getExt(path))
|
||||||
|
export const isArchive = (path) => FILE_EXTENSIONS.ARCHIVE.includes(getExt(path))
|
||||||
|
export const isDatabase = (path) => FILE_EXTENSIONS.DATABASE.includes(getExt(path))
|
||||||
|
export const isExecutable = (path) => FILE_EXTENSIONS.EXECUTABLE.includes(getExt(path))
|
||||||
|
|
||||||
|
// 复合检查
|
||||||
|
export const isVideoAny = (path) => {
|
||||||
|
const e = getExt(path)
|
||||||
|
return FILE_EXTENSIONS.VIDEO_BROWSER.includes(e) || FILE_EXTENSIONS.VIDEO_EXTERNAL.includes(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isEditableDoc = (path) => {
|
||||||
|
const e = getExt(path)
|
||||||
|
return FILE_EXTENSIONS.DOCUMENT.includes(e) && e !== 'pdf'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isBinary = (path) => isVideoAny(path) || isAudio(path) || isArchive(path) || isExecutable(path)
|
||||||
|
export const canPreview = (path) => isImage(path) || isVideo(path) || isAudio(path) || isPdf(path)
|
||||||
|
export const canEdit = (path) => !isBinary(path) && !isImage(path)
|
||||||
@@ -129,6 +129,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
// 定义组件名称,用于 KeepAlive 缓存
|
||||||
|
defineOptions({
|
||||||
|
name: 'DbCli'
|
||||||
|
})
|
||||||
|
|
||||||
import { ref, watch, provide, computed, nextTick, onMounted, onUnmounted, h, onBeforeUpdate } from 'vue'
|
import { ref, watch, provide, computed, nextTick, onMounted, onUnmounted, h, onBeforeUpdate } from 'vue'
|
||||||
import { Message, Modal } from '@arco-design/web-vue'
|
import { Message, Modal } from '@arco-design/web-vue'
|
||||||
import { IconUp, IconDown, IconCopy } from '@arco-design/web-vue/es/icon'
|
import { IconUp, IconDown, IconCopy } from '@arco-design/web-vue/es/icon'
|
||||||
|
|||||||
Reference in New Issue
Block a user