重写ssh前端组件,通用化

This commit is contained in:
dengqichen 2025-12-06 23:47:20 +08:00
parent 743255d5c5
commit 6d2f0cb95c
3 changed files with 76 additions and 32 deletions

View File

@ -4,6 +4,7 @@
*/
import React, { useRef, useState, useEffect } from 'react';
import type { LayoutOrientation } from './types';
import styles from './index.module.less';
interface TerminalSplitDividerProps {
orientation: LayoutOrientation;
@ -56,6 +57,7 @@ export const TerminalSplitDivider: React.FC<TerminalSplitDividerProps> = ({ orie
? (orientation === 'horizontal' ? container.clientWidth : container.clientHeight)
: undefined;
console.log(`[TerminalSplitDivider] onResize, orientation: ${orientation}, delta: ${delta}, containerSize: ${containerSize}`);
onResize(delta, containerSize);
lastMousePosRef.current = currentMousePos;
};
@ -79,6 +81,7 @@ export const TerminalSplitDivider: React.FC<TerminalSplitDividerProps> = ({ orie
const handleMouseDown = (e: React.MouseEvent) => {
e.preventDefault();
console.log(`[TerminalSplitDivider] mouseDown, orientation: ${orientation}`);
const initialPos = orientation === 'horizontal' ? e.clientX : e.clientY;
startPosRef.current = initialPos;
lastMousePosRef.current = initialPos;
@ -88,33 +91,8 @@ export const TerminalSplitDivider: React.FC<TerminalSplitDividerProps> = ({ orie
return (
<div
ref={dividerRef}
className={`
${orientation === 'horizontal' ? 'w-[2px] cursor-col-resize' : 'h-[2px] cursor-row-resize'}
${isDragging ? 'bg-blue-500' : 'bg-gray-400 dark:bg-gray-600 hover:bg-blue-500'}
transition-all duration-150
relative
group
flex-shrink-0
`}
className={`${styles.divider} ${styles[orientation]} ${isDragging ? styles.dragging : ''}`}
onMouseDown={handleMouseDown}
>
{/* 中心可见线 */}
<div className={`
absolute inset-0
${orientation === 'horizontal' ? 'w-[1px] left-1/2 -translate-x-1/2' : 'h-[1px] top-1/2 -translate-y-1/2'}
${isDragging ? 'bg-blue-600' : 'bg-gray-500 dark:bg-gray-500'}
`} />
{/* 拖动热区 - 增加可拖动区域 */}
<div
className={`
absolute
${orientation === 'horizontal'
? 'w-3 -left-1.5 top-0 bottom-0'
: 'h-3 left-0 right-0 -top-1.5'
}
`}
/>
</div>
/>
);
};

View File

@ -22,6 +22,21 @@
border-bottom: 1px solid #374151;
}
/* 搜索栏样式 */
.searchBar {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: #f9fafb;
border-bottom: 1px solid #e5e7eb;
}
:global(.dark) .searchBar {
background: #1f2937;
border-bottom: 1px solid #374151;
}
.left {
display: flex;
align-items: center;
@ -204,3 +219,29 @@
width: 150px;
}
}
/* 分隔线样式 */
.divider {
flex-shrink: 0;
z-index: 10;
background-color: #9ca3af;
transition: all 150ms;
&:hover {
background-color: rgb(59 130 246 / 1);
}
&.dragging {
background-color: rgb(59 130 246 / 1);
}
&.horizontal {
width: 4px;
cursor: col-resize;
}
&.vertical {
height: 4px;
cursor: row-resize;
}
}

View File

@ -6,6 +6,22 @@ import { useState, useCallback } from 'react';
import type { EditorGroup, TerminalTab, SplitDirection, SplitLayout, SplitNode, SplitContainer, LayoutOrientation } from './types';
import { TerminalInstanceManager } from './core/TerminalInstanceManager';
/**
*
* @param containerSize px
* @param orientation
* @returns px
*/
const getMinSize = (containerSize: number, orientation: LayoutOrientation): number => {
if (orientation === 'horizontal') {
// 左右分屏最小宽度520px
return 520;
} else {
// 上下分屏根据容器高度计算最小200px最多占40%
return Math.max(200, Math.min(containerSize * 0.4, 300));
}
};
interface UseSplitViewOptions {
initialTab: TerminalTab;
onWindowClose?: () => void; // 最后一个Tab关闭时的回调
@ -370,12 +386,19 @@ export const useSplitView = ({ initialTab, onWindowClose }: UseSplitViewOptions)
// 调整分屏大小
const resizeGroups = useCallback((nodeId: string, delta: number, containerSize?: number) => {
console.log(`[useSplitView.resizeGroups] nodeId: ${nodeId}, delta: ${delta}, containerSize: ${containerSize}`);
setLayout(prev => {
const result = findParent(prev.root, nodeId);
if (!result || !result.parent) return prev;
if (!result || !result.parent) {
console.log(`[useSplitView.resizeGroups] findParent failed`);
return prev;
}
const { parent, index } = result;
if (index >= parent.children.length - 1) return prev;
if (index >= parent.children.length - 1) {
console.log(`[useSplitView.resizeGroups] index check failed: ${index} >= ${parent.children.length - 1}`);
return prev;
}
const current = parent.children[index];
const next = parent.children[index + 1];
@ -385,13 +408,15 @@ export const useSplitView = ({ initialTab, onWindowClose }: UseSplitViewOptions)
// delta是像素值需要转换为百分比(delta / containerSize) * totalSize
const deltaPercent = containerSize ? (delta / containerSize) * totalSize : delta;
// 计算最小尺寸百分比:520px / 容器尺寸 * 100
// 如果没有容器尺寸使用默认20%作为兜底
const minSizePercent = containerSize ? Math.min(50, (520 / containerSize) * 100) : 20;
// 计算最小尺寸百分比:使用totalSize的20%作为最小值
// 这样每个分屏至少占两个分屏总和的20%例如总100%时最小20%
const minSizePercent = totalSize * 0.2;
const newCurrentSize = Math.max(minSizePercent, Math.min(totalSize - minSizePercent, current.size + deltaPercent));
const newNextSize = totalSize - newCurrentSize;
console.log(`[useSplitView.resizeGroups] current.size: ${current.size}, deltaPercent: ${deltaPercent}, newCurrentSize: ${newCurrentSize}, newNextSize: ${newNextSize}`);
const newChildren = parent.children.map((child, i) => {
if (i === index) return { ...child, size: newCurrentSize };
if (i === index + 1) return { ...child, size: newNextSize };