You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

363 lines
21 KiB

1 month ago
<reactive:ReactiveUserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:AuroraDesk.Presentation.ViewModels.Pages"
xmlns:converters="using:AuroraDesk.Presentation.Converters"
xmlns:reactive="using:ReactiveUI.Avalonia"
xmlns:heroicons="clr-namespace:HeroIconsAvalonia.Controls;assembly=HeroIconsAvalonia"
mc:Ignorable="d"
x:Class="AuroraDesk.Presentation.Views.Pages.SshPageView"
x:DataType="vm:SshPageViewModel"
d:DesignWidth="1200"
d:DesignHeight="800">
<reactive:ReactiveUserControl.Resources>
<converters:NullToBoolConverter x:Key="NullToBoolConverter"/>
<converters:NullToInverseBoolConverter x:Key="NullToInverseBoolConverter"/>
<converters:PlinkMessageBackgroundConverter x:Key="MessageBackgroundConverter"/>
</reactive:ReactiveUserControl.Resources>
<Grid Margin="16" RowDefinitions="Auto,16,2*">
<!-- 顶部:新建 SSH 会话 -->
<Border Grid.Row="0"
Background="White"
BorderBrush="#E5E7EB"
BorderThickness="1"
CornerRadius="10"
Padding="12"
MaxHeight="240">
<Expander IsExpanded="True"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<Expander.HeaderTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<StackPanel Orientation="Horizontal"
Spacing="6"
Margin="12,10">
<heroicons:HeroIcon Type="PlusCircle"
Width="18"
Height="18"
Foreground="#1F2937"
VerticalAlignment="Center"/>
<TextBlock Text="新建 SSH 会话"
FontSize="15"
FontWeight="SemiBold"
Foreground="#1F2937"
VerticalAlignment="Center"/>
</StackPanel>
</Grid>
</DataTemplate>
</Expander.HeaderTemplate>
<Grid Margin="0,8,0,0"
RowDefinitions="Auto,Auto,Auto"
RowSpacing="12"
ColumnDefinitions="Auto,*,24,Auto,*,24,Auto,*,24,Auto,*"
ColumnSpacing="8">
<TextBlock Grid.Row="0" Grid.Column="0" Text="主机" FontSize="12" Foreground="#6B7280" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Host}" Watermark="例如: 192.168.1.10" FontSize="12" Padding="6,4" CornerRadius="6"/>
<TextBlock Grid.Row="0" Grid.Column="3" Text="端口" FontSize="12" Foreground="#6B7280" VerticalAlignment="Center"/>
<NumericUpDown Grid.Row="0" Grid.Column="4" Value="{Binding Port}" Minimum="1" Maximum="65535" FontSize="12" Padding="6,4" CornerRadius="6" Width="110"/>
<TextBlock Grid.Row="0" Grid.Column="6" Text="用户名" FontSize="12" Foreground="#6B7280" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="7" Text="{Binding UserName}" Watermark="root" FontSize="12" Padding="6,4" CornerRadius="6"/>
<TextBlock Grid.Row="0" Grid.Column="9" Text="显示名称" FontSize="12" Foreground="#6B7280" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="10" Text="{Binding DisplayName}" Watermark="可选,默认为 user@host" FontSize="12" Padding="6,4" CornerRadius="6"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="认证方式" FontSize="12" Foreground="#6B7280" VerticalAlignment="Center"/>
<StackPanel Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Orientation="Horizontal" Spacing="16">
<RadioButton Content="密码登录"
GroupName="SshAuthModes"
IsChecked="{Binding IsPasswordMode, Mode=OneWay}"
Command="{Binding SelectPasswordAuthCommand}"/>
<RadioButton Content="私钥登录"
GroupName="SshAuthModes"
IsChecked="{Binding IsPrivateKeyMode, Mode=OneWay}"
Command="{Binding SelectPrivateKeyAuthCommand}"/>
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="6" Text="密码" FontSize="12" Foreground="#6B7280" VerticalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="7" Text="{Binding Password}" Watermark="用于密码认证,可留空" FontSize="12" Padding="6,4" CornerRadius="6" PasswordChar="●" IsEnabled="{Binding IsPasswordMode}"/>
<TextBlock Grid.Row="1" Grid.Column="9" Text="私钥口令" FontSize="12" Foreground="#6B7280" VerticalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="10" Text="{Binding PrivateKeyPassphrase}" Watermark="可选" FontSize="12" Padding="6,4" CornerRadius="6" PasswordChar="●" IsEnabled="{Binding IsPrivateKeyMode}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="私钥路径" FontSize="12" Foreground="#6B7280" VerticalAlignment="Center"/>
<TextBox Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="5" Text="{Binding PrivateKeyPath}" Watermark="例如: C:\Users\me\.ssh\id_rsa" FontSize="12" Padding="6,4" CornerRadius="6" IsEnabled="{Binding IsPrivateKeyMode}"/>
<Button Grid.Row="2"
Grid.Column="7"
Command="{Binding AddSessionCommand}"
Background="#2563EB"
Foreground="White"
BorderThickness="0"
CornerRadius="8"
Padding="10,6"
FontSize="13"
FontWeight="SemiBold"
HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
<heroicons:HeroIcon Type="PlusCircle" Width="16" Height="16" Foreground="White"/>
<TextBlock Text="创建并连接会话" FontSize="13" FontWeight="SemiBold"/>
</StackPanel>
<Button.Styles>
<Style Selector="Button:pointerover">
<Setter Property="Background" Value="#1D4ED8"/>
</Style>
<Style Selector="Button:disabled">
<Setter Property="Background" Value="#D1D5DB"/>
<Setter Property="Foreground" Value="#6B7280"/>
</Style>
</Button.Styles>
</Button>
<Border Grid.Row="2"
Grid.Column="9"
Grid.ColumnSpan="2"
Background="#F9FAFB"
BorderBrush="#E5E7EB"
BorderThickness="1"
CornerRadius="8"
Padding="6">
<TextBlock Text="{Binding StatusMessage}" FontSize="12" Foreground="#4B5563" TextWrapping="Wrap"/>
</Border>
</Grid>
</Expander>
</Border>
<!-- 底部:会话列表与详情 -->
<Grid Grid.Row="2" ColumnDefinitions="320,16,*">
<!-- 左侧:客户端列表 -->
<Border Grid.Column="0"
Background="White"
BorderBrush="#E5E7EB"
BorderThickness="1"
CornerRadius="10"
Padding="0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Background="#F9FAFB"
BorderBrush="#E5E7EB"
BorderThickness="0,0,0,1"
Padding="14,12"
CornerRadius="10,10,0,0">
<StackPanel Orientation="Horizontal" Spacing="6">
<heroicons:HeroIcon Type="ComputerDesktop" Width="16" Height="16" Foreground="#1F2937"/>
<TextBlock Text="当前会话" FontSize="14" FontWeight="SemiBold" Foreground="#1F2937"/>
<Border Background="#E0E7FF" CornerRadius="8" Padding="4,2">
<TextBlock Text="{Binding Sessions.Count}" FontSize="10" FontWeight="Bold" Foreground="#3730A3"/>
</Border>
</StackPanel>
</Border>
<ListBox Grid.Row="1"
ItemsSource="{Binding Sessions}"
SelectedItem="{Binding SelectedSession, Mode=TwoWay}"
BorderThickness="0"
Background="Transparent"
Padding="10,6">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:SshSessionItemViewModel">
<Border Background="#F8FAFC"
BorderBrush="#E2E8F0"
BorderThickness="1"
CornerRadius="8"
Padding="10"
Margin="0,0,0,8">
<StackPanel Spacing="6">
<TextBlock Text="{Binding DisplayName}"
FontSize="13"
FontWeight="SemiBold"
Foreground="#0F172A"/>
<StackPanel Orientation="Horizontal" Spacing="6">
<Border Background="#E2E8F0"
CornerRadius="6"
Padding="6,3">
<TextBlock Text="{Binding SessionId}"
FontSize="10"
Foreground="#475569"
TextTrimming="CharacterEllipsis"/>
</Border>
<Border Background="#DCFCE7"
CornerRadius="6"
Padding="6,3"
IsVisible="{Binding IsConnected}">
<TextBlock Text="已连接" FontSize="11" FontWeight="SemiBold" Foreground="#047857"/>
</Border>
</StackPanel>
<TextBlock Text="{Binding Status}" FontSize="11" Foreground="#475569"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Border Grid.Row="2"
Padding="12">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Stretch"
Spacing="8">
<Button Content="刷新"
Command="{Binding RefreshSnapshotCommand}"
Background="#E0E7FF"
Foreground="#1E3A8A"
BorderThickness="0"
CornerRadius="6"
Padding="14,8"
FontSize="12"/>
<Button Content="移除"
Command="{Binding RemoveSessionCommand}"
CommandParameter="{Binding SelectedSession}"
Background="#F87171"
Foreground="White"
BorderThickness="0"
CornerRadius="6"
Padding="14,8"
FontSize="12"
IsEnabled="{Binding SelectedSession, Converter={StaticResource NullToBoolConverter}}"/>
</StackPanel>
</Border>
</Grid>
</Border>
<!-- 右侧:会话详情 -->
<Border Grid.Column="2"
Background="White"
BorderBrush="#E5E7EB"
BorderThickness="1"
CornerRadius="12"
Padding="18">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Background="#EEF2FF"
CornerRadius="10"
Padding="14"
Margin="0,0,0,12">
<Grid ColumnDefinitions="*,Auto,Auto" ColumnSpacing="12">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Text="{Binding SelectedSession.DisplayName}"
FontSize="16"
FontWeight="SemiBold"
Foreground="#312E81"/>
<TextBlock Text="{Binding SelectedSession.SessionId}"
FontSize="11"
Foreground="#4338CA"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
<Border Grid.Column="1"
Background="#C7D2FE"
CornerRadius="8"
Padding="8,4">
<TextBlock Text="{Binding SelectedSession.Status}"
FontSize="12"
FontWeight="SemiBold"
Foreground="#1E1B4B"/>
</Border>
<Button Grid.Column="2"
Content="断开"
Command="{Binding SelectedSession.DisconnectCommand}"
Background="#F87171"
Foreground="White"
BorderThickness="0"
CornerRadius="8"
Padding="16,8"
FontSize="12"
IsEnabled="{Binding SelectedSession.IsConnected}"/>
</Grid>
</Border>
<Border Grid.Row="1"
Background="#F8FAFC"
BorderBrush="#E2E8F0"
BorderThickness="1"
CornerRadius="10"
Padding="12"
IsVisible="{Binding SelectedSession, Converter={StaticResource NullToBoolConverter}}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding SelectedSession.Messages}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:SshMessageEntry">
<Border CornerRadius="8"
Padding="10"
Margin="0,0,0,8"
Background="{Binding ., Converter={StaticResource MessageBackgroundConverter}}">
<StackPanel Spacing="4">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="{Binding Timestamp}" FontSize="11" Foreground="#6B7280"/>
<TextBlock Text="{Binding Direction}" FontSize="11" Foreground="#6B7280"/>
<Border Background="#FEE2E2"
CornerRadius="6"
Padding="6,2"
IsVisible="{Binding IsError}">
<TextBlock Text="错误"
FontSize="11"
FontWeight="SemiBold"
Foreground="#B91C1C"/>
</Border>
</StackPanel>
<TextBlock Text="{Binding Content}"
TextWrapping="Wrap"
FontSize="13"
Foreground="#0F172A"/>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Border>
<Grid Grid.Row="2"
ColumnDefinitions="*,Auto"
ColumnSpacing="10"
Margin="0,12,0,0">
<TextBox Grid.Column="0"
Text="{Binding SelectedSession.OutgoingText}"
Watermark="输入要发送的命令或文本"
AcceptsReturn="True"
TextWrapping="Wrap"
MinHeight="60"
FontSize="13"
Padding="12,10"
CornerRadius="8"/>
<Button Grid.Column="1"
Command="{Binding SelectedSession.SendCommand}"
Background="#10B981"
Foreground="White"
BorderThickness="0"
CornerRadius="10"
Padding="22,14"
FontSize="13"
FontWeight="SemiBold"
IsEnabled="{Binding SelectedSession.IsConnected}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="6">
<heroicons:HeroIcon Type="PaperAirplane" Width="16" Height="16" Foreground="White"/>
<TextBlock Text="发送" FontSize="13" FontWeight="SemiBold"/>
</StackPanel>
</Button>
</Grid>
<TextBlock Grid.Row="1"
Text="请选择左侧会话以查看实时内容。"
FontSize="14"
Foreground="#6B7280"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsVisible="{Binding SelectedSession, Converter={StaticResource NullToInverseBoolConverter}}"/>
</Grid>
</Border>
</Grid>
</Grid>
</reactive:ReactiveUserControl>