重构项目详情页面
This commit is contained in:
@@ -430,4 +430,29 @@ pub async fn get_git_diff(
|
||||
pub async fn get_git_commits(project_path: String, limit: usize) -> Result<Vec<GitCommit>, String> {
|
||||
// 使用已有的 get_git_history 函数,直接传递 limit 参数
|
||||
get_git_history(project_path, Some(limit), None).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_git_status() {
|
||||
let status_text = "?? test-untracked.txt\nA staged-file.txt\n M modified-file.txt";
|
||||
let (staged, modified, untracked, conflicted) = parse_git_status(status_text);
|
||||
|
||||
println!("Untracked files: {:?}", untracked);
|
||||
println!("Staged files: {:?}", staged);
|
||||
println!("Modified files: {:?}", modified);
|
||||
|
||||
assert_eq!(untracked.len(), 1);
|
||||
assert_eq!(untracked[0].path, "test-untracked.txt");
|
||||
assert_eq!(untracked[0].status, "untracked");
|
||||
|
||||
assert_eq!(staged.len(), 1);
|
||||
assert_eq!(staged[0].path, "staged-file.txt");
|
||||
|
||||
assert_eq!(modified.len(), 1);
|
||||
assert_eq!(modified[0].path, "modified-file.txt");
|
||||
}
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
import React, { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import {
|
||||
@@ -17,7 +16,6 @@ import {
|
||||
RefreshCw,
|
||||
Loader2,
|
||||
AlertCircle,
|
||||
GripVertical,
|
||||
FolderTree,
|
||||
FileStack,
|
||||
Maximize2,
|
||||
@@ -58,7 +56,6 @@ interface FileExplorerPanelEnhancedProps {
|
||||
onFileSelect?: (path: string) => void;
|
||||
onFileOpen?: (path: string) => void;
|
||||
onToggle: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
// 获取文件图标
|
||||
@@ -140,7 +137,6 @@ export const FileExplorerPanelEnhanced: React.FC<FileExplorerPanelEnhancedProps>
|
||||
onFileSelect,
|
||||
onFileOpen,
|
||||
onToggle,
|
||||
className,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [fileTree, setFileTree] = useState<FileNode[]>([]);
|
||||
@@ -153,57 +149,10 @@ export const FileExplorerPanelEnhanced: React.FC<FileExplorerPanelEnhancedProps>
|
||||
const [flattenedNodes, setFlattenedNodes] = useState<FileNode[]>([]);
|
||||
const [lastClickTime, setLastClickTime] = useState(0);
|
||||
const [lastClickPath, setLastClickPath] = useState<string | null>(null);
|
||||
const [width, setWidth] = useState(window.innerWidth * 0.15); // 15% of viewport width
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const [viewMode, setViewMode] = useState<"tree" | "folder">("tree");
|
||||
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const resizeHandleRef = useRef<HTMLDivElement>(null);
|
||||
const unlistenRef = useRef<UnlistenFn | null>(null);
|
||||
|
||||
// 响应窗口大小变化
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
// 保持15%的比例
|
||||
setWidth(window.innerWidth * 0.15);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
// 处理拖拽调整宽度
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (!isResizing) return;
|
||||
|
||||
const newWidth = e.clientX;
|
||||
const minWidth = window.innerWidth * 0.1; // Min 10%
|
||||
const maxWidth = window.innerWidth * 0.25; // Max 25%
|
||||
if (newWidth >= minWidth && newWidth <= maxWidth) {
|
||||
setWidth(newWidth);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
setIsResizing(false);
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
};
|
||||
|
||||
if (isResizing) {
|
||||
document.body.style.cursor = 'col-resize';
|
||||
document.body.style.userSelect = 'none';
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
};
|
||||
}, [isResizing]);
|
||||
|
||||
// 切换节点展开状态
|
||||
const toggleExpand = useCallback((path: string) => {
|
||||
setExpandedNodes((prev) => {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { AnimatePresence } from "framer-motion";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import {
|
||||
GitBranch,
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
FilePlus,
|
||||
FileX,
|
||||
FileDiff,
|
||||
GripVertical,
|
||||
Check,
|
||||
Folder,
|
||||
FolderOpen,
|
||||
@@ -139,8 +138,6 @@ export const GitPanelEnhanced: React.FC<GitPanelEnhancedProps> = ({
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [activeTab, setActiveTab] = useState("changes");
|
||||
const [width, setWidth] = useState(window.innerWidth * 0.15); // 15% of viewport width
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const [expandedNodes, setExpandedNodes] = useState<Set<string>>(new Set());
|
||||
const [selectedPath, setSelectedPath] = useState<string | null>(null);
|
||||
const [showDiffViewer, setShowDiffViewer] = useState(false);
|
||||
@@ -148,56 +145,10 @@ export const GitPanelEnhanced: React.FC<GitPanelEnhancedProps> = ({
|
||||
const [diffStaged, setDiffStaged] = useState(false);
|
||||
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const resizeHandleRef = useRef<HTMLDivElement>(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const refreshIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
// 响应窗口大小变化
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
// 保持15%的比例
|
||||
setWidth(window.innerWidth * 0.15);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
// 处理拖拽调整宽度
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (!isResizing) return;
|
||||
|
||||
const windowWidth = window.innerWidth;
|
||||
const newWidth = windowWidth - e.clientX;
|
||||
const minWidth = window.innerWidth * 0.1; // Min 10%
|
||||
const maxWidth = window.innerWidth * 0.25; // Max 25%
|
||||
|
||||
if (newWidth >= minWidth && newWidth <= maxWidth) {
|
||||
setWidth(newWidth);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
setIsResizing(false);
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
};
|
||||
|
||||
if (isResizing) {
|
||||
document.body.style.cursor = 'col-resize';
|
||||
document.body.style.userSelect = 'none';
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
};
|
||||
}, [isResizing]);
|
||||
|
||||
// 加载 Git 状态
|
||||
const loadGitStatus = useCallback(async () => {
|
||||
if (!projectPath) return;
|
||||
@@ -210,6 +161,14 @@ export const GitPanelEnhanced: React.FC<GitPanelEnhancedProps> = ({
|
||||
path: projectPath, // 修改参数名为 path
|
||||
});
|
||||
|
||||
console.log('[GitPanelEnhanced] Git status loaded:', {
|
||||
untracked: status.untracked,
|
||||
untrackedCount: status.untracked.length,
|
||||
staged: status.staged.length,
|
||||
modified: status.modified.length,
|
||||
conflicted: status.conflicted.length
|
||||
});
|
||||
|
||||
setGitStatus(status);
|
||||
} catch (err) {
|
||||
console.error("Failed to load git status:", err);
|
||||
@@ -552,9 +511,11 @@ export const GitPanelEnhanced: React.FC<GitPanelEnhancedProps> = ({
|
||||
|
||||
// 渲染文件列表(树形结构)
|
||||
const renderFileList = (files: GitFileStatus[], statusType: 'modified' | 'staged' | 'untracked' | 'conflicted') => {
|
||||
console.log(`[GitPanelEnhanced] Rendering ${statusType} files:`, files);
|
||||
if (files.length === 0) return null;
|
||||
|
||||
const fileTree = buildFileTree(files);
|
||||
console.log(`[GitPanelEnhanced] Built file tree for ${statusType}:`, fileTree);
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
@@ -659,6 +620,26 @@ export const GitPanelEnhanced: React.FC<GitPanelEnhancedProps> = ({
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{/* Debug button */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => {
|
||||
if (gitStatus) {
|
||||
console.log('=== GitPanelEnhanced Debug ===');
|
||||
console.log('Full gitStatus:', gitStatus);
|
||||
console.log('Untracked files:', gitStatus.untracked);
|
||||
console.log('Untracked count:', gitStatus.untracked.length);
|
||||
if (gitStatus.untracked.length > 0) {
|
||||
console.log('First untracked file:', gitStatus.untracked[0]);
|
||||
}
|
||||
alert(`Untracked files: ${gitStatus.untracked.length}\n${JSON.stringify(gitStatus.untracked, null, 2)}`);
|
||||
}
|
||||
}}
|
||||
className="h-7 w-7"
|
||||
>
|
||||
?
|
||||
</Button>
|
||||
{/* 展开/收起按钮 */}
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
@@ -780,6 +761,7 @@ export const GitPanelEnhanced: React.FC<GitPanelEnhancedProps> = ({
|
||||
<div className="p-2 space-y-4">
|
||||
{gitStatus && (
|
||||
<>
|
||||
{console.log('[GitPanelEnhanced] Rendering all file lists with gitStatus:', gitStatus)}
|
||||
{renderFileList(gitStatus.staged, 'staged')}
|
||||
{renderFileList(gitStatus.modified, 'modified')}
|
||||
{renderFileList(gitStatus.untracked, 'untracked')}
|
||||
|
Reference in New Issue
Block a user