Compare commits
1 Commits
main
...
feature/go
| Author | SHA1 | Date | |
|---|---|---|---|
| 9614a3d234 |
647
.kiro/specs/go-version-management/design.md
Normal file
647
.kiro/specs/go-version-management/design.md
Normal file
@ -0,0 +1,647 @@
|
|||||||
|
# Design Document: Go Version Management
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
本设计文档描述了为 PHPer 开发环境管理器添加 Go 版本管理功能的技术实现方案。该功能将允许用户安装、管理和切换不同版本的 Go 语言开发环境,与现有的 Node.js 和 Python 版本管理功能保持一致的架构和用户体验。
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### 系统架构图
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Frontend (Vue 3)"
|
||||||
|
A[GoManager.vue] --> B[Service Store]
|
||||||
|
A --> C[UI Components]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Electron Main Process"
|
||||||
|
D[IPC Handler] --> E[GoManager.ts]
|
||||||
|
E --> F[ConfigStore.ts]
|
||||||
|
E --> G[File System]
|
||||||
|
E --> H[Process Management]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "External Services"
|
||||||
|
I[golang.org API] --> E
|
||||||
|
J[Go Downloads] --> E
|
||||||
|
end
|
||||||
|
|
||||||
|
A -.->|IPC| D
|
||||||
|
E --> K[Environment Variables]
|
||||||
|
E --> L[Local Storage]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 核心架构原则
|
||||||
|
|
||||||
|
1. **一致性**: 与现有 NodeManager 和 PythonManager 保持相同的架构模式
|
||||||
|
2. **模块化**: 独立的 GoManager 服务类,便于维护和测试
|
||||||
|
3. **可扩展性**: 支持未来添加更多 Go 相关功能
|
||||||
|
4. **用户体验**: 统一的界面风格和交互模式
|
||||||
|
|
||||||
|
## Components and Interfaces
|
||||||
|
|
||||||
|
### 1. GoManager Service (Backend)
|
||||||
|
|
||||||
|
**文件位置**: `electron/services/GoManager.ts`
|
||||||
|
|
||||||
|
**主要职责**:
|
||||||
|
- 管理 Go 版本的安装、卸载和切换
|
||||||
|
- 处理环境变量配置
|
||||||
|
- 与 golang.org API 交互获取版本信息
|
||||||
|
- 文件系统操作和下载管理
|
||||||
|
|
||||||
|
**核心接口**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface GoVersion {
|
||||||
|
version: string // 版本号,如 "1.21.5"
|
||||||
|
path: string // 安装路径
|
||||||
|
isActive: boolean // 是否为当前活动版本
|
||||||
|
goroot: string // GOROOT 路径
|
||||||
|
gopath?: string // GOPATH 路径(可选)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AvailableGoVersion {
|
||||||
|
version: string // 版本号
|
||||||
|
stable: boolean // 是否为稳定版本
|
||||||
|
downloadUrl: string // Windows 64位下载链接
|
||||||
|
size: number // 文件大小(字节)
|
||||||
|
sha256: string // SHA256 校验和
|
||||||
|
}
|
||||||
|
|
||||||
|
class GoManager {
|
||||||
|
// 获取已安装版本
|
||||||
|
async getInstalledVersions(): Promise<GoVersion[]>
|
||||||
|
|
||||||
|
// 获取可用版本列表
|
||||||
|
async getAvailableVersions(): Promise<AvailableGoVersion[]>
|
||||||
|
|
||||||
|
// 安装指定版本
|
||||||
|
async install(version: string, downloadUrl: string): Promise<{success: boolean, message: string}>
|
||||||
|
|
||||||
|
// 卸载指定版本
|
||||||
|
async uninstall(version: string): Promise<{success: boolean, message: string}>
|
||||||
|
|
||||||
|
// 设置活动版本
|
||||||
|
async setActive(version: string): Promise<{success: boolean, message: string}>
|
||||||
|
|
||||||
|
// 验证 Go 安装
|
||||||
|
async validateInstallation(version: string): Promise<boolean>
|
||||||
|
|
||||||
|
// 获取 Go 环境信息
|
||||||
|
async getGoInfo(version: string): Promise<GoInfo>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. GoManager Vue Component (Frontend)
|
||||||
|
|
||||||
|
**文件位置**: `src/views/GoManager.vue`
|
||||||
|
|
||||||
|
**主要功能**:
|
||||||
|
- 显示已安装的 Go 版本列表
|
||||||
|
- 提供版本安装、卸载和切换操作
|
||||||
|
- 显示下载进度和操作状态
|
||||||
|
- 版本信息展示和管理
|
||||||
|
|
||||||
|
**组件结构**:
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<!-- 页面头部 -->
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>Go 管理</h1>
|
||||||
|
<p>管理本地 Go 版本,支持多版本切换</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 下载进度条 -->
|
||||||
|
<div v-if="downloadProgress.percent > 0" class="download-progress">
|
||||||
|
<!-- 进度显示 -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 已安装版本卡片 -->
|
||||||
|
<div class="version-grid">
|
||||||
|
<div v-for="version in versions" class="version-card">
|
||||||
|
<!-- 版本信息和操作按钮 -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 安装新版本对话框 -->
|
||||||
|
<el-dialog v-model="showInstallDialog">
|
||||||
|
<!-- 可用版本列表 -->
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. IPC Communication Layer
|
||||||
|
|
||||||
|
**主进程注册**:
|
||||||
|
```typescript
|
||||||
|
// electron/main.ts
|
||||||
|
ipcMain.handle('go:getVersions', () => goManager.getInstalledVersions())
|
||||||
|
ipcMain.handle('go:getAvailableVersions', () => goManager.getAvailableVersions())
|
||||||
|
ipcMain.handle('go:install', (_, version, url) => goManager.install(version, url))
|
||||||
|
ipcMain.handle('go:uninstall', (_, version) => goManager.uninstall(version))
|
||||||
|
ipcMain.handle('go:setActive', (_, version) => goManager.setActive(version))
|
||||||
|
```
|
||||||
|
|
||||||
|
**预加载脚本**:
|
||||||
|
```typescript
|
||||||
|
// electron/preload.ts
|
||||||
|
contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
|
go: {
|
||||||
|
getVersions: () => ipcRenderer.invoke('go:getVersions'),
|
||||||
|
getAvailableVersions: () => ipcRenderer.invoke('go:getAvailableVersions'),
|
||||||
|
install: (version: string, url: string) => ipcRenderer.invoke('go:install', version, url),
|
||||||
|
uninstall: (version: string) => ipcRenderer.invoke('go:uninstall', version),
|
||||||
|
setActive: (version: string) => ipcRenderer.invoke('go:setActive', version)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Models
|
||||||
|
|
||||||
|
### 1. Go Version Data Model
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface GoVersion {
|
||||||
|
version: string // 版本号,格式如 "1.21.5"
|
||||||
|
path: string // 本地安装路径
|
||||||
|
isActive: boolean // 是否为当前活动版本
|
||||||
|
goroot: string // GOROOT 环境变量值
|
||||||
|
gopath?: string // GOPATH 环境变量值(可选)
|
||||||
|
installDate?: Date // 安装日期
|
||||||
|
size?: number // 安装大小(字节)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GoInfo {
|
||||||
|
version: string // Go 版本
|
||||||
|
goroot: string // GOROOT 路径
|
||||||
|
gopath: string // GOPATH 路径
|
||||||
|
goversion: string // go version 命令输出
|
||||||
|
goos: string // 目标操作系统
|
||||||
|
goarch: string // 目标架构
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Available Version Data Model
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface AvailableGoVersion {
|
||||||
|
version: string // 版本号
|
||||||
|
stable: boolean // 是否为稳定版本
|
||||||
|
downloadUrl: string // Windows 64位 ZIP 下载链接
|
||||||
|
size: number // 文件大小(字节)
|
||||||
|
sha256: string // SHA256 校验和
|
||||||
|
releaseDate?: string // 发布日期
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GoRelease {
|
||||||
|
version: string
|
||||||
|
stable: boolean
|
||||||
|
files: GoFile[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GoFile {
|
||||||
|
filename: string
|
||||||
|
os: string
|
||||||
|
arch: string
|
||||||
|
version: string
|
||||||
|
sha256: string
|
||||||
|
size: number
|
||||||
|
kind: 'archive' | 'installer' | 'source'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Configuration Data Model
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface GoConfig {
|
||||||
|
activeVersion: string // 当前活动版本
|
||||||
|
installedVersions: string[] // 已安装版本列表
|
||||||
|
gopath: string // 全局 GOPATH 设置
|
||||||
|
downloadSource: 'official' // 下载源(目前仅支持官方)
|
||||||
|
autoSetGopath: boolean // 是否自动设置 GOPATH
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
### 1. Version Discovery and Download
|
||||||
|
|
||||||
|
**版本获取策略**:
|
||||||
|
1. **主要来源**: 使用 `https://golang.org/dl/?mode=json` API
|
||||||
|
2. **备用来源**: 硬编码的最新稳定版本列表
|
||||||
|
3. **缓存机制**: 5分钟本地缓存,减少 API 调用
|
||||||
|
|
||||||
|
**下载实现**:
|
||||||
|
```typescript
|
||||||
|
private async fetchGoVersions(): Promise<AvailableGoVersion[]> {
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://golang.org/dl/?mode=json')
|
||||||
|
const releases: GoRelease[] = await response.json()
|
||||||
|
|
||||||
|
return releases
|
||||||
|
.filter(release => release.stable)
|
||||||
|
.map(release => {
|
||||||
|
const windowsFile = release.files.find(f =>
|
||||||
|
f.os === 'windows' && f.arch === 'amd64' && f.kind === 'archive'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!windowsFile) return null
|
||||||
|
|
||||||
|
return {
|
||||||
|
version: release.version,
|
||||||
|
stable: release.stable,
|
||||||
|
downloadUrl: `https://golang.org/dl/${windowsFile.filename}`,
|
||||||
|
size: windowsFile.size,
|
||||||
|
sha256: windowsFile.sha256
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.slice(0, 20) // 限制显示最新 20 个版本
|
||||||
|
} catch (error) {
|
||||||
|
return this.getFallbackVersions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Installation Process
|
||||||
|
|
||||||
|
**安装流程**:
|
||||||
|
1. 验证版本是否已安装
|
||||||
|
2. 创建临时下载目录
|
||||||
|
3. 下载 Go ZIP 文件(显示进度)
|
||||||
|
4. 验证 SHA256 校验和
|
||||||
|
5. 解压到目标目录
|
||||||
|
6. 验证安装完整性
|
||||||
|
7. 更新配置文件
|
||||||
|
8. 清理临时文件
|
||||||
|
|
||||||
|
**目录结构**:
|
||||||
|
```
|
||||||
|
[PHPer安装目录]/go/
|
||||||
|
├── go-1.21.5/ # Go 1.21.5 安装目录
|
||||||
|
│ ├── bin/
|
||||||
|
│ │ ├── go.exe
|
||||||
|
│ │ ├── gofmt.exe
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── src/
|
||||||
|
│ ├── pkg/
|
||||||
|
│ └── ...
|
||||||
|
├── go-1.20.12/ # Go 1.20.12 安装目录
|
||||||
|
└── workspace/ # 默认 GOPATH 工作空间
|
||||||
|
├── src/
|
||||||
|
├── pkg/
|
||||||
|
└── bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Environment Variable Management
|
||||||
|
|
||||||
|
**环境变量配置**:
|
||||||
|
```typescript
|
||||||
|
private async updateEnvironmentVariables(goVersion: string): Promise<void> {
|
||||||
|
const goRoot = this.getGoPath(goVersion)
|
||||||
|
const goBin = join(goRoot, 'bin')
|
||||||
|
const goPath = this.getDefaultGoPath()
|
||||||
|
|
||||||
|
// 使用 PowerShell 脚本更新用户环境变量
|
||||||
|
const psScript = `
|
||||||
|
# 设置 GOROOT
|
||||||
|
[Environment]::SetEnvironmentVariable('GOROOT', '${goRoot}', 'User')
|
||||||
|
|
||||||
|
# 设置 GOPATH
|
||||||
|
[Environment]::SetEnvironmentVariable('GOPATH', '${goPath}', 'User')
|
||||||
|
|
||||||
|
# 更新 PATH
|
||||||
|
$userPath = [Environment]::GetEnvironmentVariable('PATH', 'User')
|
||||||
|
$pathArray = $userPath -split ';' | Where-Object { $_ -ne '' }
|
||||||
|
|
||||||
|
# 移除旧的 Go 路径
|
||||||
|
$filteredPaths = $pathArray | Where-Object {
|
||||||
|
-not ($_ -like '*\\go\\go-*\\bin' -or $_ -like '*\\go-*\\bin')
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加新的 Go 路径
|
||||||
|
$newPathArray = @('${goBin}') + $filteredPaths
|
||||||
|
$finalPath = ($newPathArray | Select-Object -Unique) -join ';'
|
||||||
|
|
||||||
|
[Environment]::SetEnvironmentVariable('PATH', $finalPath, 'User')
|
||||||
|
`
|
||||||
|
|
||||||
|
await this.executePowerShellScript(psScript)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Installation Validation
|
||||||
|
|
||||||
|
**验证检查项**:
|
||||||
|
1. `go.exe` 文件存在且可执行
|
||||||
|
2. `go version` 命令返回正确版本
|
||||||
|
3. `go env` 命令正常工作
|
||||||
|
4. 标准库文件完整性检查
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
private async validateInstallation(version: string): Promise<boolean> {
|
||||||
|
const goPath = this.getGoPath(version)
|
||||||
|
const goExe = join(goPath, 'bin', 'go.exe')
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 检查可执行文件
|
||||||
|
if (!existsSync(goExe)) return false
|
||||||
|
|
||||||
|
// 验证版本
|
||||||
|
const { stdout } = await execAsync(`"${goExe}" version`)
|
||||||
|
if (!stdout.includes(version)) return false
|
||||||
|
|
||||||
|
// 验证环境
|
||||||
|
await execAsync(`"${goExe}" env GOROOT`)
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Correctness Properties
|
||||||
|
|
||||||
|
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||||||
|
|
||||||
|
### Property Reflection
|
||||||
|
|
||||||
|
After analyzing all acceptance criteria, I identified several areas where properties can be consolidated to eliminate redundancy:
|
||||||
|
|
||||||
|
- **Installation validation properties** (5.1, 5.2, 5.3, 5.4) can be combined into a comprehensive installation validation property
|
||||||
|
- **Environment variable management properties** (6.1, 6.2, 6.3) can be consolidated into a single environment update property
|
||||||
|
- **Error handling properties** (8.1, 8.2, 8.3, 8.4, 8.5) share common patterns and can be grouped
|
||||||
|
- **Version list management properties** (2.4, 4.1, 4.2) can be combined into state consistency properties
|
||||||
|
|
||||||
|
### Core Properties
|
||||||
|
|
||||||
|
Property 1: **API Version Fetching Consistency**
|
||||||
|
*For any* valid API response from golang.org, the Go_Manager should correctly parse and return version information with all required fields (version, downloadUrl, size, sha256)
|
||||||
|
**Validates: Requirements 1.1**
|
||||||
|
|
||||||
|
Property 2: **Download URL Construction**
|
||||||
|
*For any* valid Go version, the Go_Manager should construct the correct Windows 64-bit download URL and initiate the download process
|
||||||
|
**Validates: Requirements 1.2**
|
||||||
|
|
||||||
|
Property 3: **Download Progress Reporting**
|
||||||
|
*For any* download operation, progress events should be emitted with valid data structures containing percentage (0-100), downloaded bytes, and total bytes
|
||||||
|
**Validates: Requirements 1.3**
|
||||||
|
|
||||||
|
Property 4: **Installation Validation Completeness**
|
||||||
|
*For any* successfully installed Go version, the validation should confirm that go.exe exists, go version returns the correct version, gofmt.exe exists, and go mod commands work
|
||||||
|
**Validates: Requirements 1.4, 5.1, 5.2, 5.3, 5.4**
|
||||||
|
|
||||||
|
Property 5: **Duplicate Installation Prevention**
|
||||||
|
*For any* Go version that is already installed, attempting to install it again should return an error message indicating the version is already installed
|
||||||
|
**Validates: Requirements 1.5**
|
||||||
|
|
||||||
|
Property 6: **File System Cleanup on Uninstall**
|
||||||
|
*For any* installed Go version, uninstalling it should completely remove the version directory and all its contents
|
||||||
|
**Validates: Requirements 2.2**
|
||||||
|
|
||||||
|
Property 7: **Active Version Environment Management**
|
||||||
|
*For any* Go version being set as active, the environment variables (PATH, GOROOT, GOPATH) should be updated correctly, and old Go paths should be removed from PATH
|
||||||
|
**Validates: Requirements 3.1, 6.1, 6.2, 6.3**
|
||||||
|
|
||||||
|
Property 8: **Version State Consistency**
|
||||||
|
*For any* operation that changes installed versions (install/uninstall), the installed versions list should be updated to reflect the current state accurately
|
||||||
|
**Validates: Requirements 2.4, 4.1**
|
||||||
|
|
||||||
|
Property 9: **Version Information Completeness**
|
||||||
|
*For any* installed Go version, the version information should include all required fields: version number, installation path, active status, GOROOT, and GOPATH
|
||||||
|
**Validates: Requirements 4.2, 4.3**
|
||||||
|
|
||||||
|
Property 10: **System Go Detection**
|
||||||
|
*For any* system with an existing Go installation, the Go_Manager should detect and display the system version information
|
||||||
|
**Validates: Requirements 4.5**
|
||||||
|
|
||||||
|
Property 11: **Environment Variable Validation**
|
||||||
|
*For any* Go version set as active, executing 'go version' in a new process should return the expected version string
|
||||||
|
**Validates: Requirements 3.2, 3.4**
|
||||||
|
|
||||||
|
Property 12: **Error Message Descriptiveness**
|
||||||
|
*For any* failed operation (network error, disk space, permissions, installation failure), the error message should be descriptive and include actionable suggestions
|
||||||
|
**Validates: Requirements 5.5, 6.5, 8.1, 8.2, 8.3, 8.5**
|
||||||
|
|
||||||
|
Property 13: **Success Message Completeness**
|
||||||
|
*For any* successful operation, the success message should include relevant details about what was accomplished
|
||||||
|
**Validates: Requirements 8.4**
|
||||||
|
|
||||||
|
Property 14: **GOPATH Default Configuration**
|
||||||
|
*For any* Go installation, when configuring GOPATH, it should be set to the user's home directory + '/go' workspace
|
||||||
|
**Validates: Requirements 6.4**
|
||||||
|
|
||||||
|
<function_calls>
|
||||||
|
<invoke name="prework">
|
||||||
|
<parameter name="featureName">go-version-management
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Error Categories and Handling Strategies
|
||||||
|
|
||||||
|
#### 1. Network Errors
|
||||||
|
- **Connection failures**: Retry mechanism with exponential backoff
|
||||||
|
- **API unavailable**: Fallback to cached version list
|
||||||
|
- **Download interruption**: Resume capability where possible
|
||||||
|
- **Timeout errors**: Configurable timeout with user feedback
|
||||||
|
|
||||||
|
#### 2. File System Errors
|
||||||
|
- **Insufficient disk space**: Pre-installation space check
|
||||||
|
- **Permission denied**: Clear guidance for administrator privileges
|
||||||
|
- **File corruption**: SHA256 verification and re-download
|
||||||
|
- **Path conflicts**: Automatic path resolution
|
||||||
|
|
||||||
|
#### 3. Installation Errors
|
||||||
|
- **Incomplete installation**: Automatic cleanup and retry option
|
||||||
|
- **Version conflicts**: Clear conflict resolution guidance
|
||||||
|
- **Environment variable failures**: Manual configuration instructions
|
||||||
|
- **Tool validation failures**: Detailed diagnostic information
|
||||||
|
|
||||||
|
#### 4. User Input Errors
|
||||||
|
- **Invalid version selection**: Input validation and user feedback
|
||||||
|
- **Concurrent operations**: Operation queuing and status indication
|
||||||
|
- **Configuration errors**: Validation with helpful error messages
|
||||||
|
|
||||||
|
### Error Recovery Mechanisms
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ErrorRecovery {
|
||||||
|
// 自动重试机制
|
||||||
|
autoRetry: {
|
||||||
|
maxAttempts: number
|
||||||
|
backoffStrategy: 'exponential' | 'linear'
|
||||||
|
retryableErrors: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户引导
|
||||||
|
userGuidance: {
|
||||||
|
errorCode: string
|
||||||
|
message: string
|
||||||
|
suggestedActions: string[]
|
||||||
|
documentationLink?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态恢复
|
||||||
|
stateRecovery: {
|
||||||
|
rollbackOnFailure: boolean
|
||||||
|
cleanupTempFiles: boolean
|
||||||
|
restoreEnvironment: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Dual Testing Approach
|
||||||
|
|
||||||
|
本项目采用单元测试和基于属性的测试相结合的方法,确保全面的代码覆盖和正确性验证。
|
||||||
|
|
||||||
|
#### Unit Testing
|
||||||
|
- **特定示例验证**: 测试具体的用例和边界条件
|
||||||
|
- **集成点测试**: 验证组件间的交互
|
||||||
|
- **错误条件测试**: 测试各种错误场景的处理
|
||||||
|
- **UI 交互测试**: 验证用户界面的响应和状态更新
|
||||||
|
|
||||||
|
#### Property-Based Testing
|
||||||
|
- **通用属性验证**: 验证在所有输入下都应该成立的属性
|
||||||
|
- **随机输入覆盖**: 通过随机化输入发现边界情况
|
||||||
|
- **状态一致性检查**: 验证系统状态的一致性
|
||||||
|
- **不变量验证**: 确保系统不变量在所有操作中保持
|
||||||
|
|
||||||
|
### Testing Framework Configuration
|
||||||
|
|
||||||
|
**Property-Based Testing Library**: 使用 `fast-check` (JavaScript/TypeScript 的属性测试库)
|
||||||
|
|
||||||
|
**测试配置要求**:
|
||||||
|
- 每个属性测试最少运行 100 次迭代
|
||||||
|
- 每个测试必须引用对应的设计文档属性
|
||||||
|
- 标签格式: `**Feature: go-version-management, Property {number}: {property_text}**`
|
||||||
|
|
||||||
|
**示例测试结构**:
|
||||||
|
```typescript
|
||||||
|
// 属性测试示例
|
||||||
|
describe('Go Version Management Properties', () => {
|
||||||
|
test('Property 1: API Version Fetching Consistency', async () => {
|
||||||
|
// **Feature: go-version-management, Property 1: API Version Fetching Consistency**
|
||||||
|
await fc.assert(fc.asyncProperty(
|
||||||
|
fc.array(fc.record({
|
||||||
|
version: fc.string(),
|
||||||
|
files: fc.array(fc.record({
|
||||||
|
os: fc.constantFrom('windows'),
|
||||||
|
arch: fc.constantFrom('amd64'),
|
||||||
|
kind: fc.constantFrom('archive'),
|
||||||
|
filename: fc.string(),
|
||||||
|
size: fc.nat(),
|
||||||
|
sha256: fc.hexaString({ minLength: 64, maxLength: 64 })
|
||||||
|
}))
|
||||||
|
})),
|
||||||
|
async (mockApiResponse) => {
|
||||||
|
const result = await goManager.parseApiResponse(mockApiResponse)
|
||||||
|
// 验证所有返回的版本都包含必需字段
|
||||||
|
result.forEach(version => {
|
||||||
|
expect(version).toHaveProperty('version')
|
||||||
|
expect(version).toHaveProperty('downloadUrl')
|
||||||
|
expect(version).toHaveProperty('size')
|
||||||
|
expect(version).toHaveProperty('sha256')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
), { numRuns: 100 })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Coverage Requirements
|
||||||
|
|
||||||
|
- **代码覆盖率**: 最低 85% 的行覆盖率
|
||||||
|
- **分支覆盖率**: 最低 80% 的分支覆盖率
|
||||||
|
- **属性覆盖率**: 所有定义的正确性属性都必须有对应的测试
|
||||||
|
- **集成测试**: 覆盖主要的用户工作流程
|
||||||
|
|
||||||
|
### Continuous Integration
|
||||||
|
|
||||||
|
- **自动化测试**: 每次代码提交都运行完整测试套件
|
||||||
|
- **性能测试**: 监控关键操作的性能指标
|
||||||
|
- **兼容性测试**: 在不同 Windows 版本上验证功能
|
||||||
|
- **回归测试**: 确保新功能不破坏现有功能
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1: Core Backend Implementation
|
||||||
|
1. 创建 `GoManager.ts` 服务类
|
||||||
|
2. 实现版本获取和解析逻辑
|
||||||
|
3. 实现下载和安装功能
|
||||||
|
4. 添加环境变量管理
|
||||||
|
5. 实现基本的错误处理
|
||||||
|
|
||||||
|
### Phase 2: Frontend Integration
|
||||||
|
1. 创建 `GoManager.vue` 组件
|
||||||
|
2. 实现版本列表显示
|
||||||
|
3. 添加安装/卸载操作界面
|
||||||
|
4. 集成下载进度显示
|
||||||
|
5. 实现状态管理和用户反馈
|
||||||
|
|
||||||
|
### Phase 3: Advanced Features
|
||||||
|
1. 添加系统 Go 检测
|
||||||
|
2. 实现高级错误处理和恢复
|
||||||
|
3. 添加配置选项和偏好设置
|
||||||
|
4. 优化性能和用户体验
|
||||||
|
5. 完善文档和帮助信息
|
||||||
|
|
||||||
|
### Phase 4: Testing and Polish
|
||||||
|
1. 实现完整的测试套件
|
||||||
|
2. 进行性能优化
|
||||||
|
3. 用户体验改进
|
||||||
|
4. 文档完善
|
||||||
|
5. 发布准备
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
### Download Security
|
||||||
|
- **HTTPS 强制**: 所有下载必须使用 HTTPS
|
||||||
|
- **SHA256 验证**: 验证下载文件的完整性
|
||||||
|
- **签名验证**: 验证 Go 官方签名(如果可用)
|
||||||
|
- **沙箱下载**: 在临时目录中处理下载文件
|
||||||
|
|
||||||
|
### Environment Security
|
||||||
|
- **权限最小化**: 仅请求必要的系统权限
|
||||||
|
- **用户级配置**: 优先使用用户级环境变量
|
||||||
|
- **路径验证**: 验证所有文件路径的安全性
|
||||||
|
- **清理机制**: 及时清理临时文件和敏感数据
|
||||||
|
|
||||||
|
### Input Validation
|
||||||
|
- **版本号验证**: 严格验证版本号格式
|
||||||
|
- **路径注入防护**: 防止路径遍历攻击
|
||||||
|
- **命令注入防护**: 安全地执行系统命令
|
||||||
|
- **配置验证**: 验证所有用户配置输入
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Optimization Strategies
|
||||||
|
- **缓存机制**: 缓存 API 响应和版本信息
|
||||||
|
- **懒加载**: 按需加载版本详细信息
|
||||||
|
- **并发控制**: 限制同时进行的下载数量
|
||||||
|
- **内存管理**: 及时释放大文件的内存占用
|
||||||
|
|
||||||
|
### Monitoring and Metrics
|
||||||
|
- **操作耗时**: 监控关键操作的执行时间
|
||||||
|
- **内存使用**: 跟踪内存使用情况
|
||||||
|
- **网络性能**: 监控下载速度和成功率
|
||||||
|
- **错误率**: 跟踪各类操作的错误率
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
### Potential Features
|
||||||
|
1. **Go 模块管理**: 集成 Go 模块和依赖管理
|
||||||
|
2. **开发工具集成**: 集成常用的 Go 开发工具
|
||||||
|
3. **项目模板**: 提供 Go 项目模板和脚手架
|
||||||
|
4. **性能分析**: 集成 Go 性能分析工具
|
||||||
|
5. **云端同步**: 同步配置和偏好设置
|
||||||
|
|
||||||
|
### Extensibility Points
|
||||||
|
- **插件系统**: 支持第三方插件扩展
|
||||||
|
- **自定义下载源**: 支持企业内部下载源
|
||||||
|
- **配置导入导出**: 支持配置的备份和恢复
|
||||||
|
- **API 扩展**: 提供扩展 API 供其他工具使用
|
||||||
112
.kiro/specs/go-version-management/requirements.md
Normal file
112
.kiro/specs/go-version-management/requirements.md
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
# Requirements Document
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
为 PHPer 开发环境管理器添加 Go 版本管理功能,使用户能够轻松安装、管理和切换不同版本的 Go 语言开发环境。该功能将与现有的 Node.js 和 Python 版本管理功能保持一致的用户体验。
|
||||||
|
|
||||||
|
## Glossary
|
||||||
|
|
||||||
|
- **Go_Manager**: Go 版本管理器,负责处理 Go 版本的安装、卸载和切换
|
||||||
|
- **Go_Version**: 特定版本的 Go 语言环境,包含编译器和标准库
|
||||||
|
- **Active_Version**: 当前系统环境变量中配置的默认 Go 版本
|
||||||
|
- **GOPATH**: Go 工作空间路径,用于存放 Go 代码和依赖包
|
||||||
|
- **GOROOT**: Go 安装根目录,包含 Go 编译器和标准库
|
||||||
|
- **Go_Binary**: Go 可执行文件,包括 go.exe、gofmt.exe 等工具
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### Requirement 1: Go 版本安装管理
|
||||||
|
|
||||||
|
**User Story:** 作为开发者,我想要安装不同版本的 Go 语言环境,以便在不同项目中使用合适的 Go 版本。
|
||||||
|
|
||||||
|
#### Acceptance Criteria
|
||||||
|
|
||||||
|
1. WHEN 用户查看可用版本列表 THEN THE Go_Manager SHALL 从 golang.org 官方 API 获取最新的 Go 版本信息
|
||||||
|
2. WHEN 用户选择安装特定版本 THEN THE Go_Manager SHALL 下载对应的 Windows 64位安装包
|
||||||
|
3. WHEN 下载过程中 THEN THE Go_Manager SHALL 显示下载进度和文件大小信息
|
||||||
|
4. WHEN 安装完成后 THEN THE Go_Manager SHALL 验证安装是否成功并显示安装结果
|
||||||
|
5. WHEN 用户尝试安装已存在的版本 THEN THE Go_Manager SHALL 提示版本已安装并阻止重复安装
|
||||||
|
|
||||||
|
### Requirement 2: Go 版本卸载管理
|
||||||
|
|
||||||
|
**User Story:** 作为开发者,我想要卸载不需要的 Go 版本,以便释放磁盘空间和保持系统整洁。
|
||||||
|
|
||||||
|
#### Acceptance Criteria
|
||||||
|
|
||||||
|
1. WHEN 用户选择卸载某个版本 THEN THE Go_Manager SHALL 显示确认对话框
|
||||||
|
2. WHEN 用户确认卸载 THEN THE Go_Manager SHALL 删除该版本的所有文件和目录
|
||||||
|
3. WHEN 卸载的是当前活动版本 THEN THE Go_Manager SHALL 清除环境变量配置
|
||||||
|
4. WHEN 卸载完成后 THEN THE Go_Manager SHALL 更新已安装版本列表
|
||||||
|
5. WHEN 用户尝试卸载不存在的版本 THEN THE Go_Manager SHALL 提示版本未安装
|
||||||
|
|
||||||
|
### Requirement 3: Go 版本切换管理
|
||||||
|
|
||||||
|
**User Story:** 作为开发者,我想要在不同的 Go 版本之间快速切换,以便在不同项目中使用不同版本的 Go。
|
||||||
|
|
||||||
|
#### Acceptance Criteria
|
||||||
|
|
||||||
|
1. WHEN 用户选择设置默认版本 THEN THE Go_Manager SHALL 更新系统环境变量 PATH
|
||||||
|
2. WHEN 环境变量更新后 THEN THE Go_Manager SHALL 验证新版本是否生效
|
||||||
|
3. WHEN 切换成功后 THEN THE Go_Manager SHALL 在界面上标识当前活动版本
|
||||||
|
4. WHEN 用户在新终端中执行 go version THEN THE System SHALL 显示新设置的 Go 版本
|
||||||
|
5. WHEN 切换版本失败 THEN THE Go_Manager SHALL 显示详细的错误信息
|
||||||
|
|
||||||
|
### Requirement 4: Go 版本信息展示
|
||||||
|
|
||||||
|
**User Story:** 作为开发者,我想要查看已安装的 Go 版本详细信息,以便了解每个版本的状态和配置。
|
||||||
|
|
||||||
|
#### Acceptance Criteria
|
||||||
|
|
||||||
|
1. WHEN 用户打开 Go 管理页面 THEN THE Go_Manager SHALL 显示所有已安装版本的列表
|
||||||
|
2. WHEN 显示版本信息时 THEN THE Go_Manager SHALL 包含版本号、安装路径和是否为活动版本
|
||||||
|
3. WHEN 显示版本信息时 THEN THE Go_Manager SHALL 显示每个版本的 GOROOT 和 GOPATH 配置
|
||||||
|
4. WHEN 版本列表为空时 THEN THE Go_Manager SHALL 显示友好的空状态提示
|
||||||
|
5. WHEN 检测到系统已安装的 Go THEN THE Go_Manager SHALL 显示系统版本信息
|
||||||
|
|
||||||
|
### Requirement 5: Go 工具链集成
|
||||||
|
|
||||||
|
**User Story:** 作为开发者,我想要确保安装的 Go 版本包含完整的工具链,以便进行完整的 Go 开发工作。
|
||||||
|
|
||||||
|
#### Acceptance Criteria
|
||||||
|
|
||||||
|
1. WHEN Go 版本安装完成后 THEN THE Go_Manager SHALL 验证 go.exe 可执行文件存在
|
||||||
|
2. WHEN Go 版本安装完成后 THEN THE Go_Manager SHALL 验证 gofmt.exe 格式化工具存在
|
||||||
|
3. WHEN Go 版本安装完成后 THEN THE Go_Manager SHALL 验证 go build 命令可正常执行
|
||||||
|
4. WHEN Go 版本安装完成后 THEN THE Go_Manager SHALL 验证 go mod 模块管理功能可用
|
||||||
|
5. WHEN 工具链验证失败 THEN THE Go_Manager SHALL 提示安装不完整并提供修复建议
|
||||||
|
|
||||||
|
### Requirement 6: 配置文件管理
|
||||||
|
|
||||||
|
**User Story:** 作为开发者,我想要系统自动管理 Go 相关的配置,以便无需手动配置复杂的环境变量。
|
||||||
|
|
||||||
|
#### Acceptance Criteria
|
||||||
|
|
||||||
|
1. WHEN 安装新的 Go 版本时 THEN THE Go_Manager SHALL 自动配置 GOROOT 环境变量
|
||||||
|
2. WHEN 切换 Go 版本时 THEN THE Go_Manager SHALL 更新用户级别的环境变量
|
||||||
|
3. WHEN 设置环境变量时 THEN THE Go_Manager SHALL 清理旧版本的路径配置
|
||||||
|
4. WHEN 配置 GOPATH 时 THEN THE Go_Manager SHALL 使用用户工作目录下的 go 文件夹
|
||||||
|
5. WHEN 环境变量配置失败 THEN THE Go_Manager SHALL 提供手动配置的指导信息
|
||||||
|
|
||||||
|
### Requirement 7: 用户界面集成
|
||||||
|
|
||||||
|
**User Story:** 作为开发者,我想要 Go 管理功能与现有界面风格保持一致,以便获得统一的用户体验。
|
||||||
|
|
||||||
|
#### Acceptance Criteria
|
||||||
|
|
||||||
|
1. WHEN 用户访问 Go 管理页面 THEN THE System SHALL 显示与其他服务管理页面一致的界面布局
|
||||||
|
2. WHEN 显示版本卡片时 THEN THE System SHALL 使用与 Node.js 管理页面相同的卡片样式
|
||||||
|
3. WHEN 显示操作按钮时 THEN THE System SHALL 使用统一的按钮样式和颜色方案
|
||||||
|
4. WHEN 显示状态信息时 THEN THE System SHALL 使用一致的图标和标签样式
|
||||||
|
5. WHEN 页面加载时 THEN THE System SHALL 支持 KeepAlive 缓存以提升切换体验
|
||||||
|
|
||||||
|
### Requirement 8: 错误处理和用户反馈
|
||||||
|
|
||||||
|
**User Story:** 作为开发者,我想要在操作过程中获得清晰的反馈信息,以便了解操作状态和处理可能的错误。
|
||||||
|
|
||||||
|
#### Acceptance Criteria
|
||||||
|
|
||||||
|
1. WHEN 网络连接失败时 THEN THE Go_Manager SHALL 显示网络错误提示并提供重试选项
|
||||||
|
2. WHEN 磁盘空间不足时 THEN THE Go_Manager SHALL 显示空间不足警告并阻止安装
|
||||||
|
3. WHEN 权限不足时 THEN THE Go_Manager SHALL 提示需要管理员权限
|
||||||
|
4. WHEN 操作成功时 THEN THE Go_Manager SHALL 显示成功消息和相关详情
|
||||||
|
5. WHEN 操作失败时 THEN THE Go_Manager SHALL 显示具体的错误原因和解决建议
|
||||||
176
.kiro/specs/go-version-management/tasks.md
Normal file
176
.kiro/specs/go-version-management/tasks.md
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# Implementation Plan: Go Version Management
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
本实现计划将 Go 版本管理功能集成到 PHPer 开发环境管理器中。实现将遵循现有的 NodeManager 和 PythonManager 架构模式,确保代码一致性和可维护性。所有代码将使用 TypeScript 编写,与项目现有技术栈保持一致。
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [x] 1. 创建 Go 管理器后端服务
|
||||||
|
- [x] 1.1 创建 GoManager.ts 服务类
|
||||||
|
- 创建 `electron/services/GoManager.ts` 文件
|
||||||
|
- 定义核心接口和类型(GoVersion, AvailableGoVersion, GoInfo)
|
||||||
|
- 实现基础类结构和 ConfigStore 集成
|
||||||
|
- _Requirements: 1.1, 4.1, 6.1_
|
||||||
|
|
||||||
|
- [x] 1.2 为核心结构编写单元测试
|
||||||
|
- 测试基础类初始化和配置集成
|
||||||
|
- _Requirements: 1.1, 4.1, 6.1_
|
||||||
|
|
||||||
|
- [x] 2. 实现版本获取和管理功能
|
||||||
|
- [x] 2.1 实现 golang.org API 集成
|
||||||
|
- 从 `https://golang.org/dl/?mode=json` 获取版本列表
|
||||||
|
- 解析 API 响应并提取 Windows 64位版本
|
||||||
|
- 实现缓存机制(5分钟缓存)
|
||||||
|
- 添加备用版本列表
|
||||||
|
- _Requirements: 1.1_
|
||||||
|
|
||||||
|
- [x] 2.2 为 API 集成编写属性测试
|
||||||
|
- **Property 1: API Version Fetching Consistency**
|
||||||
|
- **Validates: Requirements 1.1**
|
||||||
|
|
||||||
|
- [x] 2.3 实现已安装版本检测
|
||||||
|
- 扫描本地 Go 安装目录
|
||||||
|
- 验证安装完整性(go.exe, gofmt.exe 等)
|
||||||
|
- 检测当前活动版本
|
||||||
|
- 获取版本详细信息
|
||||||
|
- _Requirements: 4.1, 4.2, 4.5, 5.1, 5.2_
|
||||||
|
|
||||||
|
- [x] 2.4 为版本检测编写属性测试
|
||||||
|
- **Property 9: Version Information Completeness**
|
||||||
|
- **Validates: Requirements 4.2, 4.3**
|
||||||
|
|
||||||
|
- [x] 3. 实现下载和安装功能
|
||||||
|
- [x] 3.1 实现文件下载管理器
|
||||||
|
- 支持 HTTPS 下载和进度跟踪
|
||||||
|
- 实现 SHA256 校验和验证
|
||||||
|
- 处理下载中断和重试
|
||||||
|
- 集成到现有的下载进度系统
|
||||||
|
- _Requirements: 1.2, 1.3_
|
||||||
|
|
||||||
|
- [x] 3.2 为下载功能编写属性测试
|
||||||
|
- **Property 2: Download URL Construction**
|
||||||
|
- **Property 3: Download Progress Reporting**
|
||||||
|
- **Validates: Requirements 1.2, 1.3**
|
||||||
|
|
||||||
|
- [x] 3.3 实现 Go 版本安装逻辑
|
||||||
|
- ZIP 文件解压到目标目录
|
||||||
|
- 安装后验证和完整性检查
|
||||||
|
- 重复安装检测和防护
|
||||||
|
- 清理临时文件
|
||||||
|
- _Requirements: 1.4, 1.5, 5.1, 5.2, 5.3, 5.4_
|
||||||
|
|
||||||
|
- [x] 3.4 为安装功能编写属性测试
|
||||||
|
- **Property 4: Installation Validation Completeness**
|
||||||
|
- **Property 5: Duplicate Installation Prevention**
|
||||||
|
- **Validates: Requirements 1.4, 1.5, 5.1, 5.2, 5.3, 5.4**
|
||||||
|
|
||||||
|
- [x] 4. 实现环境变量管理
|
||||||
|
- [x] 4.1 实现环境变量更新逻辑
|
||||||
|
- 使用 PowerShell 脚本更新用户环境变量
|
||||||
|
- 设置 GOROOT、GOPATH 和 PATH
|
||||||
|
- 清理旧版本的路径配置
|
||||||
|
- _Requirements: 3.1, 6.1, 6.2, 6.3, 6.4_
|
||||||
|
|
||||||
|
- [x] 4.2 为环境变量管理编写属性测试
|
||||||
|
- **Property 7: Active Version Environment Management**
|
||||||
|
- **Property 14: GOPATH Default Configuration**
|
||||||
|
- **Validates: Requirements 3.1, 6.1, 6.2, 6.3, 6.4**
|
||||||
|
|
||||||
|
- [x] 4.3 实现版本切换和验证
|
||||||
|
- 版本激活和环境变量更新
|
||||||
|
- 切换后的功能验证
|
||||||
|
- 系统级 Go 命令验证
|
||||||
|
- _Requirements: 3.2, 3.3, 3.4_
|
||||||
|
|
||||||
|
- [x] 4.4 为版本切换编写属性测试
|
||||||
|
- **Property 11: Environment Variable Validation**
|
||||||
|
- **Validates: Requirements 3.2, 3.4**
|
||||||
|
|
||||||
|
- [x] 5. 实现卸载和清理功能
|
||||||
|
- [x] 5.1 实现版本卸载逻辑
|
||||||
|
- 文件系统清理和目录删除
|
||||||
|
- 活动版本的环境变量清理
|
||||||
|
- 配置状态更新
|
||||||
|
- _Requirements: 2.2, 2.3, 2.4, 2.5_
|
||||||
|
|
||||||
|
- [x] 5.2 为卸载功能编写属性测试
|
||||||
|
- **Property 6: File System Cleanup on Uninstall**
|
||||||
|
- **Property 8: Version State Consistency**
|
||||||
|
- **Validates: Requirements 2.2, 2.4**
|
||||||
|
|
||||||
|
- [x] 6. 实现错误处理和用户反馈
|
||||||
|
- [x] 6.1 实现综合错误处理系统
|
||||||
|
- 网络错误、磁盘空间、权限检查
|
||||||
|
- 描述性错误消息和解决建议
|
||||||
|
- 成功操作的详细反馈
|
||||||
|
- _Requirements: 8.1, 8.2, 8.3, 8.4, 8.5_
|
||||||
|
|
||||||
|
- [x] 7. 实现 IPC 通信层
|
||||||
|
- [x] 7.1 在主进程中注册 Go 管理 IPC 处理器
|
||||||
|
- 在 `electron/main.ts` 中添加 Go 相关的 IPC 处理函数
|
||||||
|
- 注册 go:getVersions, go:getAvailableVersions, go:install, go:uninstall, go:setActive 等
|
||||||
|
- 集成 GoManager 实例
|
||||||
|
- _Requirements: 所有后端功能_
|
||||||
|
|
||||||
|
- [x] 7.2 更新预加载脚本
|
||||||
|
- 在 `electron/preload.ts` 中暴露 Go 管理 API
|
||||||
|
- 添加 go 对象到 electronAPI
|
||||||
|
- 确保类型安全的 IPC 通信
|
||||||
|
- _Requirements: 所有后端功能_
|
||||||
|
|
||||||
|
- [x] 8. 实现前端 Vue 组件
|
||||||
|
- [x] 8.1 创建 GoManager.vue 组件
|
||||||
|
- 创建 `src/views/GoManager.vue` 文件
|
||||||
|
- 设置组件模板和基本布局(参考 NodeManager.vue 和 PythonManager.vue)
|
||||||
|
- 实现 KeepAlive 缓存支持
|
||||||
|
- 添加响应式数据管理
|
||||||
|
- _Requirements: 7.1, 7.5_
|
||||||
|
|
||||||
|
- [x] 8.2 实现版本列表显示功能
|
||||||
|
- 已安装版本的卡片式展示
|
||||||
|
- 版本信息显示(版本号、路径、状态)
|
||||||
|
- 活动版本的视觉标识
|
||||||
|
- 空状态处理和友好提示
|
||||||
|
- _Requirements: 4.1, 4.2, 4.3, 4.4_
|
||||||
|
|
||||||
|
- [x] 8.3 实现版本操作界面
|
||||||
|
- 安装新版本对话框
|
||||||
|
- 可用版本列表和选择
|
||||||
|
- 卸载确认对话框
|
||||||
|
- 版本切换操作按钮
|
||||||
|
- _Requirements: 1.1, 2.1, 3.2_
|
||||||
|
|
||||||
|
- [x] 8.4 实现下载进度和状态反馈
|
||||||
|
- 下载进度条和状态显示
|
||||||
|
- 操作加载状态和按钮禁用
|
||||||
|
- 成功/错误消息提示
|
||||||
|
- 集成到现有的下载进度监听系统
|
||||||
|
- _Requirements: 1.3, 8.4, 8.5_
|
||||||
|
|
||||||
|
- [x] 9. 集成到主应用程序
|
||||||
|
- [x] 9.1 添加 Go 管理页面路由
|
||||||
|
- 在 `src/router/index.ts` 中添加 Go 管理路由
|
||||||
|
- 路径: `/go`, 名称: `go`, 组件: `GoManager.vue`
|
||||||
|
- 添加页面标题元数据
|
||||||
|
- _Requirements: 7.1_
|
||||||
|
|
||||||
|
- [x] 9.2 更新应用程序导航
|
||||||
|
- 在主导航中添加 Go 管理入口
|
||||||
|
- 添加 Go 相关的图标
|
||||||
|
- 确保视觉一致性
|
||||||
|
- _Requirements: 7.2, 7.3, 7.4_
|
||||||
|
|
||||||
|
- [x] 10. 检查点 - 完整功能验证
|
||||||
|
- 确保所有功能正常工作
|
||||||
|
- 验证用户体验和界面一致性
|
||||||
|
- 询问用户是否有问题或需要调整
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- 后端 GoManager 服务已完全实现,包含所有核心功能和错误处理
|
||||||
|
- 属性测试已实现,覆盖所有关键正确性属性
|
||||||
|
- 需要完成 IPC 通信层、前端组件和应用集成
|
||||||
|
- 每个任务都引用了具体的需求条目以确保可追溯性
|
||||||
|
- 所有代码将使用 TypeScript 编写,与项目技术栈保持一致
|
||||||
|
- 实现将遵循现有的 NodeManager 和 PythonManager 架构模式
|
||||||
24
.kiro/steering/product.md
Normal file
24
.kiro/steering/product.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# PHPer 开发环境管理器
|
||||||
|
|
||||||
|
PHPer 开发环境管理器是一款专为 Windows 平台设计的 PHP 开发环境管理工具,提供可视化界面来管理多种开发服务和工具。
|
||||||
|
|
||||||
|
## 核心功能
|
||||||
|
|
||||||
|
- **多版本 PHP 管理**: 支持 PHP 8.1-8.5 多版本并行安装,独立 CGI 进程控制
|
||||||
|
- **数据库服务**: MySQL 5.7/8.0 版本管理,自动初始化和配置
|
||||||
|
- **Web 服务器**: Nginx 管理,支持虚拟主机和 SSL 证书
|
||||||
|
- **缓存服务**: Redis Windows 版本管理
|
||||||
|
- **开发工具**: Node.js、Python、Git 版本管理
|
||||||
|
- **站点管理**: 可视化创建和管理开发站点,支持 Laravel 项目
|
||||||
|
- **系统集成**: Hosts 文件管理,开机自启动配置
|
||||||
|
|
||||||
|
## 目标用户
|
||||||
|
|
||||||
|
主要面向 Windows 平台的 PHP 开发者,提供类似 XAMPP/WAMP 但更现代化和可定制的开发环境解决方案。
|
||||||
|
|
||||||
|
## 技术特点
|
||||||
|
|
||||||
|
- 基于 Electron 的桌面应用
|
||||||
|
- 使用 Vue 3 + TypeScript 构建前端界面
|
||||||
|
- 服务管理采用 Windows 原生进程控制
|
||||||
|
- 支持静默启动和系统托盘运行
|
||||||
105
.kiro/steering/structure.md
Normal file
105
.kiro/steering/structure.md
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# 项目结构和组织
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
### 核心目录
|
||||||
|
|
||||||
|
```
|
||||||
|
phper/
|
||||||
|
├── electron/ # Electron 主进程代码
|
||||||
|
│ ├── main.ts # 主进程入口,IPC 处理
|
||||||
|
│ ├── preload.ts # 预加载脚本,安全的 IPC 桥接
|
||||||
|
│ └── services/ # 服务管理模块
|
||||||
|
│ ├── ConfigStore.ts # 配置存储管理
|
||||||
|
│ ├── ServiceManager.ts # 统一服务管理
|
||||||
|
│ ├── *Manager.ts # 各服务专用管理器
|
||||||
|
│ └── __tests__/ # 服务层单元测试
|
||||||
|
│
|
||||||
|
├── src/ # Vue 前端源码
|
||||||
|
│ ├── App.vue # 根组件,包含布局和导航
|
||||||
|
│ ├── main.ts # 前端入口文件
|
||||||
|
│ ├── router/ # Vue Router 配置
|
||||||
|
│ ├── stores/ # Pinia 状态管理
|
||||||
|
│ ├── components/ # 可复用组件
|
||||||
|
│ ├── views/ # 页面组件
|
||||||
|
│ └── styles/ # 全局样式和主题
|
||||||
|
│
|
||||||
|
├── data/ # 运行时数据目录
|
||||||
|
├── service/ # 服务安装目录
|
||||||
|
├── build/ # 构建资源
|
||||||
|
├── release/ # 打包输出
|
||||||
|
└── scripts/ # 构建脚本
|
||||||
|
```
|
||||||
|
|
||||||
|
## 架构模式
|
||||||
|
|
||||||
|
### Electron 架构
|
||||||
|
- **主进程** (`electron/main.ts`): 窗口管理、系统集成、IPC 处理
|
||||||
|
- **渲染进程** (`src/`): Vue 应用,用户界面
|
||||||
|
- **预加载脚本** (`electron/preload.ts`): 安全的 API 暴露
|
||||||
|
|
||||||
|
### 服务管理架构
|
||||||
|
- **ServiceManager**: 统一的服务生命周期管理
|
||||||
|
- **专用管理器**: 每个服务(PHP、MySQL、Nginx等)有独立的管理类
|
||||||
|
- **ConfigStore**: 集中的配置管理,基于 electron-store
|
||||||
|
|
||||||
|
### 前端架构
|
||||||
|
- **组件化**: 使用 Vue 3 Composition API
|
||||||
|
- **状态管理**: Pinia stores 管理应用状态
|
||||||
|
- **路由管理**: Vue Router 4 单页面应用
|
||||||
|
- **UI 组件**: Element Plus 提供一致的界面体验
|
||||||
|
|
||||||
|
## 命名约定
|
||||||
|
|
||||||
|
### 文件命名
|
||||||
|
- **组件文件**: PascalCase (如 `PhpManager.vue`)
|
||||||
|
- **服务文件**: PascalCase + Manager 后缀 (如 `MysqlManager.ts`)
|
||||||
|
- **工具文件**: camelCase (如 `configStore.ts`)
|
||||||
|
- **测试文件**: `*.test.ts` 后缀
|
||||||
|
|
||||||
|
### 代码约定
|
||||||
|
- **接口**: `I` 前缀 (如 `IServiceStatus`)
|
||||||
|
- **类型**: PascalCase (如 `ServiceConfig`)
|
||||||
|
- **常量**: UPPER_SNAKE_CASE
|
||||||
|
- **函数/变量**: camelCase
|
||||||
|
|
||||||
|
## 关键文件说明
|
||||||
|
|
||||||
|
### 配置文件
|
||||||
|
- `package.json`: 项目依赖和构建脚本
|
||||||
|
- `vite.config.ts`: Vite 构建配置,包含 Electron 插件
|
||||||
|
- `tsconfig.json`: TypeScript 编译配置
|
||||||
|
- `jest.config.js`: 测试框架配置
|
||||||
|
|
||||||
|
### 入口文件
|
||||||
|
- `index.html`: HTML 模板
|
||||||
|
- `src/main.ts`: Vue 应用入口
|
||||||
|
- `electron/main.ts`: Electron 主进程入口
|
||||||
|
|
||||||
|
### 服务管理
|
||||||
|
- 每个服务管理器负责:下载、安装、配置、启动/停止、状态检查
|
||||||
|
- 使用 Windows 原生命令和进程管理
|
||||||
|
- 支持多版本并存(PHP、MySQL、Node.js)
|
||||||
|
|
||||||
|
## 数据流
|
||||||
|
|
||||||
|
1. **用户操作** → Vue 组件
|
||||||
|
2. **组件事件** → Pinia Store 或直接 IPC 调用
|
||||||
|
3. **IPC 通信** → Electron 主进程
|
||||||
|
4. **服务调用** → 对应的 Manager 类
|
||||||
|
5. **系统操作** → Windows 命令/进程
|
||||||
|
6. **状态更新** → 通过 IPC 返回到前端
|
||||||
|
|
||||||
|
## 扩展指南
|
||||||
|
|
||||||
|
### 添加新服务
|
||||||
|
1. 创建 `*Manager.ts` 在 `electron/services/`
|
||||||
|
2. 在 `main.ts` 中注册 IPC 处理程序
|
||||||
|
3. 创建对应的 Vue 页面组件
|
||||||
|
4. 添加路由和导航菜单项
|
||||||
|
5. 更新 `ServiceManager.ts` 集成新服务
|
||||||
|
|
||||||
|
### 添加新功能
|
||||||
|
- 前端功能:在 `src/views/` 添加页面组件
|
||||||
|
- 后端功能:在对应的 Manager 类中添加方法
|
||||||
|
- 通用组件:在 `src/components/` 创建可复用组件
|
||||||
84
.kiro/steering/tech.md
Normal file
84
.kiro/steering/tech.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# 技术栈和构建系统
|
||||||
|
|
||||||
|
## 核心技术栈
|
||||||
|
|
||||||
|
### 前端框架
|
||||||
|
- **Vue 3**: 使用 Composition API 和 `<script setup>` 语法
|
||||||
|
- **TypeScript**: 严格类型检查,配置在 `tsconfig.json`
|
||||||
|
- **Element Plus**: UI 组件库,提供现代化界面组件
|
||||||
|
- **Vue Router 4**: 单页面应用路由管理
|
||||||
|
- **Pinia**: 状态管理,替代 Vuex
|
||||||
|
|
||||||
|
### 桌面应用框架
|
||||||
|
- **Electron**: 主进程和渲染进程架构
|
||||||
|
- **IPC 通信**: 主进程与渲染进程间的安全通信
|
||||||
|
- **electron-store**: 应用配置持久化存储
|
||||||
|
|
||||||
|
### 构建工具
|
||||||
|
- **Vite**: 现代化构建工具,支持热重载
|
||||||
|
- **vite-plugin-electron**: Electron 集成插件
|
||||||
|
- **electron-builder**: 应用打包和分发
|
||||||
|
|
||||||
|
### 开发工具
|
||||||
|
- **Jest**: 单元测试框架,配置在 `jest.config.js`
|
||||||
|
- **vue-tsc**: Vue 组件类型检查
|
||||||
|
- **Sass**: CSS 预处理器,支持主题变量
|
||||||
|
|
||||||
|
## 常用命令
|
||||||
|
|
||||||
|
### 开发环境
|
||||||
|
```bash
|
||||||
|
# 启动开发服务器
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# 类型检查
|
||||||
|
npm run typecheck
|
||||||
|
|
||||||
|
# 运行测试
|
||||||
|
npm run test
|
||||||
|
npm run test:watch
|
||||||
|
```
|
||||||
|
|
||||||
|
### 构建和打包
|
||||||
|
```bash
|
||||||
|
# 构建生产版本(自动更新 patch 版本)
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 指定版本更新类型
|
||||||
|
npm run build:patch # 1.0.0 -> 1.0.1
|
||||||
|
npm run build:minor # 1.0.0 -> 1.1.0
|
||||||
|
npm run build:major # 1.0.0 -> 2.0.0
|
||||||
|
|
||||||
|
# 不更新版本号直接打包
|
||||||
|
npm run build:nobump
|
||||||
|
|
||||||
|
# 构建前进行类型检查
|
||||||
|
npm run build:check
|
||||||
|
```
|
||||||
|
|
||||||
|
## 关键依赖
|
||||||
|
|
||||||
|
### 生产依赖
|
||||||
|
- `axios`: HTTP 客户端
|
||||||
|
- `unzipper`: 文件解压缩
|
||||||
|
- `node-windows`: Windows 服务管理
|
||||||
|
- `sudo-prompt`: 管理员权限提升
|
||||||
|
|
||||||
|
### 开发依赖
|
||||||
|
- `concurrently`: 并行运行多个命令
|
||||||
|
- `fast-check`: 属性测试库
|
||||||
|
- `ts-jest`: TypeScript Jest 支持
|
||||||
|
|
||||||
|
## 构建配置
|
||||||
|
|
||||||
|
- **输出目录**: `dist` (前端), `dist-electron` (Electron)
|
||||||
|
- **图标**: `build/icon.ico`
|
||||||
|
- **安装包**: 生成到 `release` 目录
|
||||||
|
- **目标平台**: Windows x64 (NSIS 和 Portable)
|
||||||
|
|
||||||
|
## 开发注意事项
|
||||||
|
|
||||||
|
- 使用 `windowsHide: true` 隐藏命令行窗口
|
||||||
|
- 服务启动使用 VBScript 实现静默启动
|
||||||
|
- 需要管理员权限进行服务管理和 hosts 文件修改
|
||||||
|
- 支持开机自启动(任务计划程序)
|
||||||
@ -17,6 +17,7 @@ import { ServiceManager } from "./services/ServiceManager";
|
|||||||
import { HostsManager } from "./services/HostsManager";
|
import { HostsManager } from "./services/HostsManager";
|
||||||
import { GitManager } from "./services/GitManager";
|
import { GitManager } from "./services/GitManager";
|
||||||
import { PythonManager } from "./services/PythonManager";
|
import { PythonManager } from "./services/PythonManager";
|
||||||
|
import { GoManager } from "./services/GoManager";
|
||||||
import { LogManager } from "./services/LogManager";
|
import { LogManager } from "./services/LogManager";
|
||||||
import { ConfigStore } from "./services/ConfigStore";
|
import { ConfigStore } from "./services/ConfigStore";
|
||||||
|
|
||||||
@ -121,6 +122,7 @@ const serviceManager = new ServiceManager(configStore);
|
|||||||
const hostsManager = new HostsManager();
|
const hostsManager = new HostsManager();
|
||||||
const gitManager = new GitManager(configStore);
|
const gitManager = new GitManager(configStore);
|
||||||
const pythonManager = new PythonManager(configStore);
|
const pythonManager = new PythonManager(configStore);
|
||||||
|
const goManager = new GoManager(configStore);
|
||||||
const logManager = new LogManager(configStore);
|
const logManager = new LogManager(configStore);
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
@ -566,6 +568,30 @@ ipcMain.handle(
|
|||||||
pythonManager.installPackage(version, packageName)
|
pythonManager.installPackage(version, packageName)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ==================== Go 管理 ====================
|
||||||
|
ipcMain.handle("go:getVersions", () => goManager.getInstalledVersions());
|
||||||
|
ipcMain.handle("go:getAvailableVersions", () =>
|
||||||
|
goManager.getAvailableVersions()
|
||||||
|
);
|
||||||
|
ipcMain.handle("go:install", (_, version: string, downloadUrl: string, expectedSha256?: string) =>
|
||||||
|
goManager.install(version, downloadUrl, expectedSha256)
|
||||||
|
);
|
||||||
|
ipcMain.handle("go:uninstall", (_, version: string) =>
|
||||||
|
goManager.uninstall(version)
|
||||||
|
);
|
||||||
|
ipcMain.handle("go:setActive", (_, version: string) =>
|
||||||
|
goManager.setActive(version)
|
||||||
|
);
|
||||||
|
ipcMain.handle("go:validateInstallation", (_, version: string) =>
|
||||||
|
goManager.validateInstallation(version)
|
||||||
|
);
|
||||||
|
ipcMain.handle("go:getInfo", (_, version: string) =>
|
||||||
|
goManager.getGoInfo(version)
|
||||||
|
);
|
||||||
|
ipcMain.handle("go:detectSystemVersion", () =>
|
||||||
|
goManager.detectSystemGoVersion()
|
||||||
|
);
|
||||||
|
|
||||||
// ==================== 配置管理 ====================
|
// ==================== 配置管理 ====================
|
||||||
ipcMain.handle("config:get", (_, key: string) => configStore.get(key));
|
ipcMain.handle("config:get", (_, key: string) => configStore.get(key));
|
||||||
ipcMain.handle("config:set", (_, key: string, value: any) =>
|
ipcMain.handle("config:set", (_, key: string, value: any) =>
|
||||||
|
|||||||
@ -133,6 +133,18 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
installPackage: (version: string, packageName: string) => ipcRenderer.invoke('python:installPackage', version, packageName)
|
installPackage: (version: string, packageName: string) => ipcRenderer.invoke('python:installPackage', version, packageName)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Go 管理
|
||||||
|
go: {
|
||||||
|
getVersions: () => ipcRenderer.invoke('go:getVersions'),
|
||||||
|
getAvailableVersions: () => ipcRenderer.invoke('go:getAvailableVersions'),
|
||||||
|
install: (version: string, downloadUrl: string, expectedSha256?: string) => ipcRenderer.invoke('go:install', version, downloadUrl, expectedSha256),
|
||||||
|
uninstall: (version: string) => ipcRenderer.invoke('go:uninstall', version),
|
||||||
|
setActive: (version: string) => ipcRenderer.invoke('go:setActive', version),
|
||||||
|
validateInstallation: (version: string) => ipcRenderer.invoke('go:validateInstallation', version),
|
||||||
|
getInfo: (version: string) => ipcRenderer.invoke('go:getInfo', version),
|
||||||
|
detectSystemVersion: () => ipcRenderer.invoke('go:detectSystemVersion')
|
||||||
|
},
|
||||||
|
|
||||||
// 服务管理
|
// 服务管理
|
||||||
service: {
|
service: {
|
||||||
getAll: () => ipcRenderer.invoke('service:getAll'),
|
getAll: () => ipcRenderer.invoke('service:getAll'),
|
||||||
@ -213,6 +225,7 @@ const api = {
|
|||||||
mysql: {} as any,
|
mysql: {} as any,
|
||||||
nginx: {} as any,
|
nginx: {} as any,
|
||||||
redis: {} as any,
|
redis: {} as any,
|
||||||
|
go: {} as any,
|
||||||
service: {} as any,
|
service: {} as any,
|
||||||
hosts: {} as any,
|
hosts: {} as any,
|
||||||
config: {} as any
|
config: {} as any
|
||||||
|
|||||||
@ -10,8 +10,10 @@ interface ConfigSchema {
|
|||||||
nginxVersions: string[];
|
nginxVersions: string[];
|
||||||
redisVersions: string[];
|
redisVersions: string[];
|
||||||
nodeVersions: string[];
|
nodeVersions: string[];
|
||||||
|
goVersions: string[];
|
||||||
activePhpVersion: string;
|
activePhpVersion: string;
|
||||||
activeNodeVersion: string;
|
activeNodeVersion: string;
|
||||||
|
activeGoVersion: string;
|
||||||
autoStart: {
|
autoStart: {
|
||||||
nginx: boolean;
|
nginx: boolean;
|
||||||
mysql: boolean;
|
mysql: boolean;
|
||||||
@ -66,8 +68,10 @@ export class ConfigStore {
|
|||||||
nginxVersions: [],
|
nginxVersions: [],
|
||||||
redisVersions: [],
|
redisVersions: [],
|
||||||
nodeVersions: [],
|
nodeVersions: [],
|
||||||
|
goVersions: [],
|
||||||
activePhpVersion: "",
|
activePhpVersion: "",
|
||||||
activeNodeVersion: "",
|
activeNodeVersion: "",
|
||||||
|
activeGoVersion: "",
|
||||||
autoStart: {
|
autoStart: {
|
||||||
nginx: false,
|
nginx: false,
|
||||||
mysql: false,
|
mysql: false,
|
||||||
@ -99,6 +103,7 @@ export class ConfigStore {
|
|||||||
join(this.basePath, "nginx", "ssl"),
|
join(this.basePath, "nginx", "ssl"),
|
||||||
join(this.basePath, "redis"),
|
join(this.basePath, "redis"),
|
||||||
join(this.basePath, "nodejs"),
|
join(this.basePath, "nodejs"),
|
||||||
|
join(this.basePath, "go"),
|
||||||
join(this.basePath, "logs"),
|
join(this.basePath, "logs"),
|
||||||
join(this.basePath, "temp"),
|
join(this.basePath, "temp"),
|
||||||
join(this.basePath, "www"),
|
join(this.basePath, "www"),
|
||||||
@ -149,6 +154,10 @@ export class ConfigStore {
|
|||||||
return join(this.basePath, "nodejs");
|
return join(this.basePath, "nodejs");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getGoPath(): string {
|
||||||
|
return join(this.basePath, "go");
|
||||||
|
}
|
||||||
|
|
||||||
getLogsPath(): string {
|
getLogsPath(): string {
|
||||||
return join(this.basePath, "logs");
|
return join(this.basePath, "logs");
|
||||||
}
|
}
|
||||||
|
|||||||
2048
electron/services/GoManager.ts
Normal file
2048
electron/services/GoManager.ts
Normal file
File diff suppressed because it is too large
Load Diff
1617
electron/services/GoManager.ts.backup
Normal file
1617
electron/services/GoManager.ts.backup
Normal file
File diff suppressed because it is too large
Load Diff
1065
electron/services/__tests__/GoManager.test.ts
Normal file
1065
electron/services/__tests__/GoManager.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
16
jest.config.js
Normal file
16
jest.config.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
roots: ['<rootDir>/electron'],
|
||||||
|
testMatch: ['**/__tests__/**/*.test.ts'],
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'electron/services/**/*.ts',
|
||||||
|
'!electron/services/**/*.test.ts',
|
||||||
|
'!electron/services/__tests__/**'
|
||||||
|
],
|
||||||
|
moduleFileExtensions: ['ts', 'js'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': 'ts-jest'
|
||||||
|
},
|
||||||
|
testTimeout: 30000
|
||||||
|
}
|
||||||
3691
package-lock.json
generated
3691
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,18 +14,24 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"electron:dev": "vite",
|
"electron:dev": "vite",
|
||||||
"electron:build": "node scripts/bump-version.js && vite build && electron-builder",
|
"electron:build": "node scripts/bump-version.js && vite build && electron-builder",
|
||||||
"typecheck": "vue-tsc --noEmit"
|
"typecheck": "vue-tsc --noEmit",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch"
|
||||||
},
|
},
|
||||||
"author": "PHPer",
|
"author": "PHPer",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^20.10.0",
|
"@types/node": "^20.10.0",
|
||||||
"@vitejs/plugin-vue": "^4.5.0",
|
"@vitejs/plugin-vue": "^4.5.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"electron": "^28.0.0",
|
"electron": "^28.0.0",
|
||||||
"electron-builder": "^24.9.1",
|
"electron-builder": "^24.9.1",
|
||||||
|
"fast-check": "^4.5.3",
|
||||||
|
"jest": "^30.2.0",
|
||||||
"rcedit": "^5.0.2",
|
"rcedit": "^5.0.2",
|
||||||
"sass": "^1.69.5",
|
"sass": "^1.69.5",
|
||||||
|
"ts-jest": "^29.4.6",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^5.0.0",
|
"vite": "^5.0.0",
|
||||||
"vite-plugin-electron": "^0.15.5",
|
"vite-plugin-electron": "^0.15.5",
|
||||||
|
|||||||
@ -92,6 +92,7 @@ const cachedViews = [
|
|||||||
'RedisManager',
|
'RedisManager',
|
||||||
'NodeManager',
|
'NodeManager',
|
||||||
'PythonManager',
|
'PythonManager',
|
||||||
|
'GoManager',
|
||||||
'GitManager',
|
'GitManager',
|
||||||
'SitesManager',
|
'SitesManager',
|
||||||
'HostsManager',
|
'HostsManager',
|
||||||
@ -113,6 +114,7 @@ const menuItems = [
|
|||||||
{ path: '/redis', label: 'Redis 管理', icon: 'Grid', service: 'redis' },
|
{ path: '/redis', label: 'Redis 管理', icon: 'Grid', service: 'redis' },
|
||||||
{ path: '/nodejs', label: 'Node.js 管理', icon: 'Promotion', service: null },
|
{ path: '/nodejs', label: 'Node.js 管理', icon: 'Promotion', service: null },
|
||||||
{ path: '/python', label: 'Python 管理', icon: 'Platform', service: null },
|
{ path: '/python', label: 'Python 管理', icon: 'Platform', service: null },
|
||||||
|
{ path: '/go', label: 'Go 管理', icon: 'Box', service: null },
|
||||||
{ path: '/git', label: 'Git 管理', icon: 'Share', service: null },
|
{ path: '/git', label: 'Git 管理', icon: 'Share', service: null },
|
||||||
{ path: '/sites', label: '站点管理', icon: 'Monitor', service: null },
|
{ path: '/sites', label: '站点管理', icon: 'Monitor', service: null },
|
||||||
{ path: '/hosts', label: 'Hosts 管理', icon: 'Document', service: null },
|
{ path: '/hosts', label: 'Hosts 管理', icon: 'Document', service: null },
|
||||||
|
|||||||
@ -63,6 +63,12 @@ const router = createRouter({
|
|||||||
component: () => import('@/views/PythonManager.vue'),
|
component: () => import('@/views/PythonManager.vue'),
|
||||||
meta: { title: 'Python 管理' }
|
meta: { title: 'Python 管理' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/go',
|
||||||
|
name: 'go',
|
||||||
|
component: () => import('@/views/GoManager.vue'),
|
||||||
|
meta: { title: 'Go 管理' }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
name: 'settings',
|
name: 'settings',
|
||||||
|
|||||||
@ -30,6 +30,15 @@ interface NodeVersion {
|
|||||||
isActive: boolean
|
isActive: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go 版本信息
|
||||||
|
interface GoVersion {
|
||||||
|
version: string
|
||||||
|
path: string
|
||||||
|
isActive: boolean
|
||||||
|
goroot: string
|
||||||
|
gopath?: string
|
||||||
|
}
|
||||||
|
|
||||||
// 站点信息
|
// 站点信息
|
||||||
interface SiteConfig {
|
interface SiteConfig {
|
||||||
name: string
|
name: string
|
||||||
@ -56,6 +65,9 @@ export const useServiceStore = defineStore('service', () => {
|
|||||||
// Node.js 版本列表
|
// Node.js 版本列表
|
||||||
const nodeVersions = ref<NodeVersion[]>([])
|
const nodeVersions = ref<NodeVersion[]>([])
|
||||||
|
|
||||||
|
// Go 版本列表
|
||||||
|
const goVersions = ref<GoVersion[]>([])
|
||||||
|
|
||||||
// 站点列表
|
// 站点列表
|
||||||
const sites = ref<SiteConfig[]>([])
|
const sites = ref<SiteConfig[]>([])
|
||||||
|
|
||||||
@ -99,6 +111,11 @@ export const useServiceStore = defineStore('service', () => {
|
|||||||
return nodeVersions.value.find(v => v.isActive)
|
return nodeVersions.value.find(v => v.isActive)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 计算属性:当前活动的 Go 版本
|
||||||
|
const activeGoVersion = computed(() => {
|
||||||
|
return goVersions.value.find(v => v.isActive)
|
||||||
|
})
|
||||||
|
|
||||||
// 刷新所有状态
|
// 刷新所有状态
|
||||||
async function refreshAll() {
|
async function refreshAll() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@ -107,6 +124,7 @@ export const useServiceStore = defineStore('service', () => {
|
|||||||
refreshServiceStatus(),
|
refreshServiceStatus(),
|
||||||
refreshPhpVersions(),
|
refreshPhpVersions(),
|
||||||
refreshNodeVersions(),
|
refreshNodeVersions(),
|
||||||
|
refreshGoVersions(),
|
||||||
refreshSites(),
|
refreshSites(),
|
||||||
refreshBasePath()
|
refreshBasePath()
|
||||||
])
|
])
|
||||||
@ -175,6 +193,18 @@ export const useServiceStore = defineStore('service', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 刷新 Go 版本列表
|
||||||
|
async function refreshGoVersions() {
|
||||||
|
try {
|
||||||
|
const versions = await window.electronAPI?.go.getVersions()
|
||||||
|
if (versions) {
|
||||||
|
goVersions.value = versions
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('刷新 Go 版本失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 刷新站点列表
|
// 刷新站点列表
|
||||||
async function refreshSites() {
|
async function refreshSites() {
|
||||||
try {
|
try {
|
||||||
@ -217,6 +247,7 @@ export const useServiceStore = defineStore('service', () => {
|
|||||||
serviceStatus,
|
serviceStatus,
|
||||||
phpVersions,
|
phpVersions,
|
||||||
nodeVersions,
|
nodeVersions,
|
||||||
|
goVersions,
|
||||||
sites,
|
sites,
|
||||||
basePath,
|
basePath,
|
||||||
loading,
|
loading,
|
||||||
@ -227,11 +258,13 @@ export const useServiceStore = defineStore('service', () => {
|
|||||||
runningServiceCount,
|
runningServiceCount,
|
||||||
activePhpVersion,
|
activePhpVersion,
|
||||||
activeNodeVersion,
|
activeNodeVersion,
|
||||||
|
activeGoVersion,
|
||||||
// 方法
|
// 方法
|
||||||
refreshAll,
|
refreshAll,
|
||||||
refreshServiceStatus,
|
refreshServiceStatus,
|
||||||
refreshPhpVersions,
|
refreshPhpVersions,
|
||||||
refreshNodeVersions,
|
refreshNodeVersions,
|
||||||
|
refreshGoVersions,
|
||||||
refreshSites,
|
refreshSites,
|
||||||
refreshBasePath,
|
refreshBasePath,
|
||||||
updateServiceStatus,
|
updateServiceStatus,
|
||||||
|
|||||||
519
src/views/GoManager.vue
Normal file
519
src/views/GoManager.vue
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container">
|
||||||
|
<div class="page-header">
|
||||||
|
<h1 class="page-title">
|
||||||
|
<span class="title-icon"><el-icon><Platform /></el-icon></span>
|
||||||
|
Go 管理
|
||||||
|
</h1>
|
||||||
|
<p class="page-description">管理本地 Go 版本,支持多版本切换</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 下载进度 -->
|
||||||
|
<div v-if="downloadProgress.percent > 0 && downloadProgress.percent < 100" class="download-progress">
|
||||||
|
<div class="progress-info">
|
||||||
|
<span>正在下载 Go...</span>
|
||||||
|
<span>{{ formatSize(downloadProgress.downloaded) }} / {{ formatSize(downloadProgress.total) }}</span>
|
||||||
|
</div>
|
||||||
|
<el-progress :percentage="downloadProgress.percent" :stroke-width="10" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 已安装版本 -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">已安装版本</span>
|
||||||
|
<el-button type="primary" @click="showInstallDialog = true">
|
||||||
|
<el-icon><Plus /></el-icon>
|
||||||
|
安装新版本
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div v-if="loading" class="loading-state">
|
||||||
|
<el-icon class="is-loading"><Loading /></el-icon>
|
||||||
|
<span>加载中...</span>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="versions.length === 0" class="empty-state">
|
||||||
|
<el-icon class="empty-icon"><Platform /></el-icon>
|
||||||
|
<h3 class="empty-title">暂未安装 Go</h3>
|
||||||
|
<p class="empty-description">点击上方按钮安装第一个 Go 版本</p>
|
||||||
|
</div>
|
||||||
|
<div v-else class="version-grid">
|
||||||
|
<div
|
||||||
|
v-for="version in versions"
|
||||||
|
:key="version.version"
|
||||||
|
class="version-card"
|
||||||
|
:class="{ active: version.isActive }"
|
||||||
|
>
|
||||||
|
<div class="version-main">
|
||||||
|
<div class="version-icon">
|
||||||
|
<el-icon :size="32"><Platform /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="version-content">
|
||||||
|
<div class="version-title">
|
||||||
|
<span class="version-number">Go {{ version.version }}</span>
|
||||||
|
<el-tag v-if="version.isActive" type="success" size="small" effect="dark">当前版本</el-tag>
|
||||||
|
</div>
|
||||||
|
<div class="version-meta">
|
||||||
|
<div class="version-path">
|
||||||
|
<el-icon><FolderOpened /></el-icon>
|
||||||
|
<span>{{ version.path }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="version.goroot" class="goroot-info">
|
||||||
|
<el-icon><Files /></el-icon>
|
||||||
|
<span>GOROOT: {{ version.goroot }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="version.gopath" class="gopath-info">
|
||||||
|
<el-icon><Box /></el-icon>
|
||||||
|
<span>GOPATH: {{ version.gopath }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="version-actions">
|
||||||
|
<el-button
|
||||||
|
v-if="!version.isActive"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="setActiveVersion(version.version)"
|
||||||
|
:loading="settingActive === version.version"
|
||||||
|
>
|
||||||
|
设为默认
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
plain
|
||||||
|
@click="uninstallVersion(version.version)"
|
||||||
|
:loading="uninstalling === version.version"
|
||||||
|
>
|
||||||
|
卸载
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 安装新版本对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="showInstallDialog"
|
||||||
|
title="安装 Go"
|
||||||
|
width="700px"
|
||||||
|
>
|
||||||
|
<el-alert type="info" :closable="false" class="mb-4">
|
||||||
|
<template #title>
|
||||||
|
<el-icon><InfoFilled /></el-icon>
|
||||||
|
下载源说明
|
||||||
|
</template>
|
||||||
|
Go 将从官方网站 <a href="https://golang.org/dl/" target="_blank">golang.org</a> 下载 Windows 64位版本。
|
||||||
|
</el-alert>
|
||||||
|
<div v-if="loadingAvailableVersions" class="loading-state">
|
||||||
|
<el-icon class="is-loading"><Loading /></el-icon>
|
||||||
|
<span>正在获取可用版本列表...</span>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="availableVersions.length === 0" class="empty-hint">
|
||||||
|
<span>暂无可用版本</span>
|
||||||
|
</div>
|
||||||
|
<div v-else class="available-versions">
|
||||||
|
<el-table :data="availableVersions" style="width: 100%" max-height="400">
|
||||||
|
<el-table-column prop="version" label="版本" width="120" />
|
||||||
|
<el-table-column label="类型" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag v-if="row.stable" type="success" size="small">稳定版</el-tag>
|
||||||
|
<el-tag v-else type="warning" size="small">开发版</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="大小" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatSize(row.size) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button
|
||||||
|
v-if="!isInstalled(row.version)"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="installVersion(row)"
|
||||||
|
:loading="installing === row.version"
|
||||||
|
>
|
||||||
|
安装
|
||||||
|
</el-button>
|
||||||
|
<el-tag v-else type="info" size="small">已安装</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="showInstallDialog = false">关闭</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { Plus, Platform, InfoFilled, Loading, FolderOpened, Files, Box } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
// 定义组件名称以便 KeepAlive 正确缓存
|
||||||
|
defineOptions({
|
||||||
|
name: 'GoManager'
|
||||||
|
})
|
||||||
|
|
||||||
|
interface GoVersion {
|
||||||
|
version: string
|
||||||
|
path: string
|
||||||
|
isActive: boolean
|
||||||
|
goroot: string
|
||||||
|
gopath?: string
|
||||||
|
installDate?: Date
|
||||||
|
size?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AvailableGoVersion {
|
||||||
|
version: string
|
||||||
|
stable: boolean
|
||||||
|
downloadUrl: string
|
||||||
|
size: number
|
||||||
|
sha256: string
|
||||||
|
releaseDate?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const versions = ref<GoVersion[]>([])
|
||||||
|
const availableVersions = ref<AvailableGoVersion[]>([])
|
||||||
|
const showInstallDialog = ref(false)
|
||||||
|
const installing = ref('')
|
||||||
|
const uninstalling = ref('')
|
||||||
|
const settingActive = ref('')
|
||||||
|
|
||||||
|
const downloadProgress = reactive({
|
||||||
|
percent: 0,
|
||||||
|
downloaded: 0,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadVersions = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
versions.value = await window.electronAPI?.go.getVersions() || []
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('加载版本失败:', error)
|
||||||
|
ElMessage.error('加载已安装版本失败: ' + error.message)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadingAvailableVersions = ref(false)
|
||||||
|
|
||||||
|
const loadAvailableVersions = async () => {
|
||||||
|
loadingAvailableVersions.value = true
|
||||||
|
try {
|
||||||
|
availableVersions.value = await window.electronAPI?.go.getAvailableVersions() || []
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('加载可用版本失败:', error)
|
||||||
|
ElMessage.error('加载可用版本失败: ' + error.message)
|
||||||
|
} finally {
|
||||||
|
loadingAvailableVersions.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInstalled = (version: string) => {
|
||||||
|
return versions.value.some(v => v.version === version)
|
||||||
|
}
|
||||||
|
|
||||||
|
const installVersion = async (row: AvailableGoVersion) => {
|
||||||
|
installing.value = row.version
|
||||||
|
downloadProgress.percent = 0
|
||||||
|
downloadProgress.downloaded = 0
|
||||||
|
downloadProgress.total = 0
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await window.electronAPI?.go.install(row.version, row.downloadUrl, row.sha256)
|
||||||
|
if (result?.success) {
|
||||||
|
ElMessage.success(result.message)
|
||||||
|
await loadVersions()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(result?.message || '安装失败')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
ElMessage.error(error.message)
|
||||||
|
} finally {
|
||||||
|
installing.value = ''
|
||||||
|
downloadProgress.percent = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uninstallVersion = async (version: string) => {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`确定要卸载 Go ${version} 吗?`,
|
||||||
|
'确认卸载',
|
||||||
|
{ type: 'warning' }
|
||||||
|
)
|
||||||
|
|
||||||
|
uninstalling.value = version
|
||||||
|
const result = await window.electronAPI?.go.uninstall(version)
|
||||||
|
if (result?.success) {
|
||||||
|
ElMessage.success(result.message)
|
||||||
|
await loadVersions()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(result?.message || '卸载失败')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error !== 'cancel') {
|
||||||
|
ElMessage.error(error.message)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
uninstalling.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setActiveVersion = async (version: string) => {
|
||||||
|
settingActive.value = version
|
||||||
|
try {
|
||||||
|
const result = await window.electronAPI?.go.setActive(version)
|
||||||
|
if (result?.success) {
|
||||||
|
ElMessage.success(result.message)
|
||||||
|
await loadVersions()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(result?.message || '设置失败')
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
ElMessage.error(error.message)
|
||||||
|
} finally {
|
||||||
|
settingActive.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatSize = (bytes: number) => {
|
||||||
|
if (bytes === 0) return '0 B'
|
||||||
|
const k = 1024
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听下载进度
|
||||||
|
const onDownloadProgress = (_event: any, data: any) => {
|
||||||
|
if (data.type === 'go') {
|
||||||
|
downloadProgress.percent = data.progress
|
||||||
|
downloadProgress.downloaded = data.downloaded
|
||||||
|
downloadProgress.total = data.total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadVersions()
|
||||||
|
loadAvailableVersions()
|
||||||
|
window.electronAPI?.onDownloadProgress(onDownloadProgress)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.electronAPI?.removeDownloadProgressListener(onDownloadProgress)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.loading-state {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 40px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
|
||||||
|
.is-loading {
|
||||||
|
font-size: 24px;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 20px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: 64px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-description {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-card {
|
||||||
|
background: var(--bg-input);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 24px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: var(--success-color);
|
||||||
|
background: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, rgba(16, 185, 129, 0.02) 100%);
|
||||||
|
|
||||||
|
.version-icon {
|
||||||
|
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-main {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: linear-gradient(135deg, #00add8 0%, #007d9c 100%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-number {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-primary);
|
||||||
|
letter-spacing: -0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-path,
|
||||||
|
.goroot-info,
|
||||||
|
.gopath-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
background: var(--bg-card);
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-family: 'Fira Code', monospace;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
color: var(--accent-color);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
padding-top: 4px;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.available-versions {
|
||||||
|
.el-table {
|
||||||
|
--el-table-bg-color: transparent;
|
||||||
|
--el-table-tr-bg-color: transparent;
|
||||||
|
--el-table-header-bg-color: var(--bg-input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-progress {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.progress-info {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-hint {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 20px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-4 {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--accent-color);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user