fix(frontend): 终端窗口壳问题修复
- 详情抽屉打开终端/RDP/文件浏览前先关闭抽屉,修复同 z-index 遮挡冲突 - 删除语义不通的黄灯按钮,绿灯随全屏状态切换 aria-label - 连接指示点补充 role=status 与 sr-only 文案,不再仅靠颜色区分 - LiveMonitorDialog 接入 i18n;修复 TerminalDialog 乱码注释;全屏文案迁至 common 命名空间
This commit is contained in:
1 parent
115e8b451c
commit
0563a29de1
4 files changed
+46
-29
No files matched your search
@@ -1,4 +1,5 @@
|
||||
// macOS 风格终端标题栏:红黄绿 + user@host:~ // Logan.Li
|
||||
// macOS 风格终端标题栏:红绿 + user@host:~。终端为固定深色主题,
|
||||
// 此处的颜色常量是终端专属暗色 token,不跟随全局主题(豁免 var(--dg-*))。 // Logan.Li
|
||||
import type { ReactNode } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
@@ -23,6 +24,9 @@ interface TerminalWindowChromeProps {
|
||||
closeLabel: string;
|
||||
fullscreenLabel: string;
|
||||
restoreLabel: string;
|
||||
/** 连接状态的无障碍文案,与红/绿指示点配套,避免仅靠颜色区分 // Logan.Li */
|
||||
connectedLabel?: string;
|
||||
disconnectedLabel?: string;
|
||||
}
|
||||
|
||||
export function TerminalWindowChrome({
|
||||
@@ -35,7 +39,10 @@ export function TerminalWindowChrome({
|
||||
closeLabel,
|
||||
fullscreenLabel,
|
||||
restoreLabel,
|
||||
connectedLabel,
|
||||
disconnectedLabel,
|
||||
}: TerminalWindowChromeProps) {
|
||||
// 弹窗没有“最小化”语义,只保留红(关闭)绿(全屏切换)两盏灯,去掉误导性黄灯。 // Logan.Li
|
||||
return (
|
||||
<div
|
||||
className="flex items-center gap-2 border-b px-4 py-2.5"
|
||||
@@ -50,19 +57,8 @@ export function TerminalWindowChrome({
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={restoreLabel}
|
||||
title={restoreLabel}
|
||||
disabled={!fullscreen}
|
||||
onClick={() => fullscreen && onToggleFullscreen()}
|
||||
className={cn(
|
||||
'h-3 w-3 shrink-0 rounded-full bg-[#febc2e]',
|
||||
fullscreen ? 'hover:brightness-110' : 'opacity-70',
|
||||
)}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={fullscreenLabel}
|
||||
title={fullscreenLabel}
|
||||
aria-label={fullscreen ? restoreLabel : fullscreenLabel}
|
||||
title={fullscreen ? restoreLabel : fullscreenLabel}
|
||||
onClick={onToggleFullscreen}
|
||||
className="h-3 w-3 shrink-0 rounded-full bg-[#28c840] hover:brightness-110"
|
||||
/>
|
||||
@@ -71,11 +67,20 @@ export function TerminalWindowChrome({
|
||||
</span>
|
||||
{connected != null && (
|
||||
<span
|
||||
className={cn(
|
||||
'h-1.5 w-1.5 shrink-0 rounded-full',
|
||||
connected ? 'bg-[#28c840]' : 'bg-[#ff5f57]',
|
||||
role="status"
|
||||
title={connected ? connectedLabel : disconnectedLabel}
|
||||
className="flex shrink-0 items-center gap-1"
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
'h-1.5 w-1.5 rounded-full',
|
||||
connected ? 'bg-[#28c840]' : 'bg-[#ff5f57]',
|
||||
)}
|
||||
/>
|
||||
{(connected ? connectedLabel : disconnectedLabel) && (
|
||||
<span className="sr-only">{connected ? connectedLabel : disconnectedLabel}</span>
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{trailing && <div className="ml-auto flex min-w-0 items-center gap-1">{trailing}</div>}
|
||||
</div>
|
||||
|
||||
@@ -128,7 +128,11 @@ export function ServerManager() {
|
||||
const getServerDisplayName = (server: JsonData) =>
|
||||
server.alias || server.cloud_instance_name || server.hostname || server.ip || "";
|
||||
|
||||
// 详情抽屉内打开终端/RDP/文件时先关闭抽屉,避免同 z-index 的 Sheet 遮挡弹窗 // Logan.Li
|
||||
const closeDetailSheet = () => setDetailServer(null);
|
||||
|
||||
const openTerminal = (server: JsonData) => {
|
||||
closeDetailSheet();
|
||||
setTerminalServer({
|
||||
id: server.id,
|
||||
name: getServerDisplayName(server),
|
||||
@@ -140,11 +144,13 @@ export function ServerManager() {
|
||||
};
|
||||
|
||||
const openRdp = (server: JsonData) => {
|
||||
closeDetailSheet();
|
||||
setRdpServer({ id: server.id, name: getServerDisplayName(server), ip: server.ip });
|
||||
setRdpOpen(true);
|
||||
};
|
||||
|
||||
const openFileBrowser = (server: JsonData) => {
|
||||
closeDetailSheet();
|
||||
setFileBrowserServer({ id: server.id, name: getServerDisplayName(server) });
|
||||
setFileBrowserOpen(true);
|
||||
};
|
||||
|
||||
@@ -38,7 +38,7 @@ interface TerminalDialogProps {
|
||||
serverName: string;
|
||||
serverIp: string;
|
||||
username?: string;
|
||||
/** ????????????????? SSH ??? // Logan.Li */
|
||||
/** 资产是否已配置 SSH 凭据,用于终端连接前的判断 // Logan.Li */
|
||||
hasAuth?: boolean;
|
||||
}
|
||||
|
||||
@@ -227,8 +227,10 @@ export function TerminalDialog({
|
||||
onToggleFullscreen={() => setFullscreen(!fullscreen)}
|
||||
onClose={() => onOpenChange(false)}
|
||||
closeLabel={t("common.close")}
|
||||
fullscreenLabel={t("server.detailSheet.fullscreen")}
|
||||
restoreLabel={t("server.detailSheet.exitFullscreen")}
|
||||
fullscreenLabel={t("common.fullscreen")}
|
||||
restoreLabel={t("common.exitFullscreen")}
|
||||
connectedLabel={t("common.connected")}
|
||||
disconnectedLabel={t("common.disconnected")}
|
||||
/>
|
||||
{!authReady && (
|
||||
<div
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from '@/components/ui/dialog';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
TerminalWindowChrome,
|
||||
@@ -40,6 +41,7 @@ interface LiveMonitorProps {
|
||||
}
|
||||
|
||||
export function LiveMonitorDialog({ open, onOpenChange, sessionId, sessionInfo }: LiveMonitorProps) {
|
||||
const { t } = useTranslation();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const termRef = useRef<XTerm | null>(null);
|
||||
const fitRef = useRef<FitAddon | null>(null);
|
||||
@@ -198,17 +200,19 @@ export function LiveMonitorDialog({ open, onOpenChange, sessionId, sessionInfo }
|
||||
: { maxWidth: '960px', width: '92vw' }),
|
||||
}}
|
||||
>
|
||||
<DialogTitle className="sr-only">终端实时监控</DialogTitle>
|
||||
<DialogDescription className="sr-only">审计在线 WebSSH 会话</DialogDescription>
|
||||
<DialogTitle className="sr-only">{t('system.liveMonitor.title')}</DialogTitle>
|
||||
<DialogDescription className="sr-only">{t('system.liveMonitor.description')}</DialogDescription>
|
||||
<TerminalWindowChrome
|
||||
sessionTitle={`${meta?.username || 'monitor'}@${meta?.server_name || 'host'}:~`}
|
||||
connected={connected}
|
||||
fullscreen={fullscreen}
|
||||
onToggleFullscreen={() => setFullscreen(!fullscreen)}
|
||||
onClose={() => onOpenChange(false)}
|
||||
closeLabel="关闭"
|
||||
fullscreenLabel="全屏"
|
||||
restoreLabel="退出全屏"
|
||||
closeLabel={t('common.close')}
|
||||
fullscreenLabel={t('common.fullscreen')}
|
||||
restoreLabel={t('common.exitFullscreen')}
|
||||
connectedLabel={t('common.connected')}
|
||||
disconnectedLabel={t('common.disconnected')}
|
||||
trailing={
|
||||
<>
|
||||
{meta && (
|
||||
@@ -218,18 +222,18 @@ export function LiveMonitorDialog({ open, onOpenChange, sessionId, sessionInfo }
|
||||
)}
|
||||
{connected && (
|
||||
<Badge variant="outline" className="border-emerald-500/30 bg-emerald-500/10 text-xs text-emerald-400">
|
||||
在线
|
||||
{t('common.online')}
|
||||
</Badge>
|
||||
)}
|
||||
{!connected && !connectError && (
|
||||
<Badge variant="outline" className="border-yellow-500/30 bg-yellow-500/10 text-xs text-yellow-400">
|
||||
<Loader2 className="mr-1 h-3 w-3 animate-spin" />
|
||||
连接中
|
||||
{t('system.liveMonitor.connecting')}
|
||||
</Badge>
|
||||
)}
|
||||
{connectError && (
|
||||
<Badge variant="outline" className="border-red-500/30 bg-red-500/10 text-xs text-red-400">
|
||||
连接失败
|
||||
{t('system.liveMonitor.connectFailed')}
|
||||
</Badge>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in new issue
Block a user