瀏覽代碼

新增刷卡登录窗口及刷卡器配置管理功能

本次提交实现了刷卡登录窗口及其动画界面,支持刷卡器串口配置的读取与保存,并在配置服务中增加相关接口和实现。同时完善了串口协议的数据接收事件,支持刷卡数据实时传递。更新了工程文件以包含新View和ViewModel,优化了部分代码结构和线程安全处理。为后续刷卡登录功能扩展打下基础。
孝锋 徐 7 月之前
父節點
當前提交
163e025c8d

+ 5 - 0
TeamAAS-VM/Core/Lights/SerialPortProtocol.cs

@@ -132,7 +132,12 @@ namespace TeamAAS_VP.Core.Lights
                     {
                         CacheTimeout = TimeSpan.FromMilliseconds(100)
                     });
+                _client.Received = async (c, e) =>
+                {
 
+                    string Data = e.ByteBlock.Span.ToString(Encoding); //接收到数据
+                    DataReceived?.Invoke(this, Data.Trim());
+                };
                 _client.Setup(config);
 
                 var result = await _client.TryConnectAsync();

+ 12 - 0
TeamAAS-VM/Interfaces/IConfigService.cs

@@ -477,5 +477,17 @@ namespace TeamAAS_VP.Interfaces
         /// 检查剩余螺丝是否报警
         /// </summary>
         bool CheckAlarm(int feederNumber);
+
+        //刷卡登录
+        /// <summary>
+        /// 获取刷卡器配置
+        /// </summary>
+        /// <returns></returns>
+        SerialPortConfig GetCardReaderConfig();
+        /// <summary>
+        /// 保存刷卡器配置
+        /// </summary>
+        /// <param name="config"></param>
+        void SaveCardReaderConfig(SerialPortConfig config);
     }
 }

+ 78 - 3
TeamAAS-VM/Services/ConfigService.cs

@@ -46,6 +46,8 @@ namespace TeamAAS_VP.Services
             public static readonly string ScrewDriverConfigurationPath = "..//Config//ScrewDriverConfiguration.cfg";
             // 螺丝供料器(物料)配置路径
             public static readonly string ScrewFeedersConfigurationPath = "..//Config//ScrewFeedersConfiguration.cfg";
+            // 刷卡器配置路径
+            public static readonly string CardReaderConfigurationPath = "..//Config//CardReaderConfiguration.cfg";
         }
 
         private readonly object _sync = new object();
@@ -67,10 +69,13 @@ namespace TeamAAS_VP.Services
         private PlcAddressConfig PlcAddressConfig { get; set; }
         // 单个电批配置
         private TeamAAS_VP.Models.ScrewDriver.ScrewDriverConfig _screwDriverConfig;
-        public TeamAAS_VP.Models.ScrewDriver.ScrewDriverConfig ScrewDriverConfig { get { lock(_sync) { return _screwDriverConfig; } } }
+        public TeamAAS_VP.Models.ScrewDriver.ScrewDriverConfig ScrewDriverConfig { get { lock (_sync) { return _screwDriverConfig; } } }
 
         private ISystemDatabaseService _systemDatabaseService;
 
+        // 刷卡器配置
+        private SerialPortConfig _cardReaderConfig;
+
         /// <summary>
         /// 注入系统数据库服务(可选),用于记录螺丝批次变更等记录。
         /// </summary>
@@ -123,7 +128,7 @@ namespace TeamAAS_VP.Services
                     Feeders = new ObservableCollection<FeederInfo>();
                     FileHelper.WriteJsonFile(Feeders, ConfigPaths.FeedersConfigurationPath);
                 }
-                
+
                 // load screw feeders (material) configuration
                 if (File.Exists(ConfigPaths.ScrewFeedersConfigurationPath))
                     ScrewFeeders = FileHelper.ReadJsonFile<ObservableCollection<ScrewFeederInfo>>(ConfigPaths.ScrewFeedersConfigurationPath);
@@ -203,6 +208,16 @@ namespace TeamAAS_VP.Services
                     _deviceInfo = new DeviceInfo();
                     FileHelper.WriteJsonFile(_deviceInfo, ConfigPaths.DeviceInfoConfigurationPath);
                 }
+
+                // load card reader configuration
+                if (File.Exists(ConfigPaths.CardReaderConfigurationPath))
+                    _cardReaderConfig = FileHelper.ReadJsonFile<SerialPortConfig>(ConfigPaths.CardReaderConfigurationPath);
+                else
+                {
+                    _cardReaderConfig = new SerialPortConfig();
+                    FileHelper.WriteJsonFile(_cardReaderConfig, ConfigPaths.CardReaderConfigurationPath);
+                }
+
             }
         }
 
@@ -241,6 +256,9 @@ namespace TeamAAS_VP.Services
                 // save device info
                 if (_deviceInfo != null)
                     FileHelper.WriteJsonFile(_deviceInfo, ConfigPaths.DeviceInfoConfigurationPath);
+                // save card reader config
+                if (_cardReaderConfig != null)
+                    FileHelper.WriteJsonFile(_cardReaderConfig, ConfigPaths.CardReaderConfigurationPath);
             }
         }
 
@@ -1229,7 +1247,7 @@ namespace TeamAAS_VP.Services
                 var f = ScrewFeeders.FirstOrDefault(s => s.FeederNumber == feederNumber);
                 if (f == null) return;
                 f.BatchNumber = batchNumber;
-                previousCount= f.RemainingCount;
+                previousCount = f.RemainingCount;
                 f.RemainingCount += Math.Max(0, f.PackSize);
 
                 // prepare capture values
@@ -1303,6 +1321,63 @@ namespace TeamAAS_VP.Services
         }
         #endregion
 
+        #region 用户登录刷卡器
+        /// <summary>
+        /// 获取刷卡器配置
+        /// </summary>
+        /// <returns></returns>
+        public SerialPortConfig GetCardReaderConfig()
+        {
+            lock (_sync)
+            {
+                if (_cardReaderConfig != null)
+                    return _cardReaderConfig;
+
+                if (File.Exists(ConfigPaths.CardReaderConfigurationPath))
+                {
+                    try
+                    {
+                        _cardReaderConfig = FileHelper.ReadJsonFile<SerialPortConfig>(ConfigPaths.CardReaderConfigurationPath);
+                    }
+                    catch
+                    {
+                        // 若读取失败,创建默认配置并覆盖文件(与项目中其他配置策略一致)
+                        _cardReaderConfig = new SerialPortConfig();
+                        try { FileHelper.WriteJsonFile(_cardReaderConfig, ConfigPaths.CardReaderConfigurationPath); } catch { }
+                    }
+                }
+                else
+                {
+                    _cardReaderConfig = new SerialPortConfig();
+                    try { FileHelper.WriteJsonFile(_cardReaderConfig, ConfigPaths.CardReaderConfigurationPath); } catch { }
+                }
+
+                return _cardReaderConfig;
+            }
+        }
+
+        /// <summary>
+        /// 保存刷卡器配置
+        /// </summary>
+        /// <param name="config"></param>
+        public void SaveCardReaderConfig(SerialPortConfig config)
+        {
+            if (config == null) return;
+            lock (_sync)
+            {
+                _cardReaderConfig = config;
+                try
+                {
+                    FileHelper.WriteJsonFile(_cardReaderConfig, ConfigPaths.CardReaderConfigurationPath);
+                }
+                catch
+                {
+                    // 忽略持久化错误(与项目中其它保存方法一致),可在需要时添加日志记录
+                }
+            }
+        }
+        #endregion
+
         /// <summary>
         /// 释放资源并保存当前内存中的所有配置到文件。
         /// </summary>

+ 8 - 0
TeamAAS-VM/TeamAAS-VP.csproj

@@ -546,6 +546,7 @@
       <DependentUpon>Lang.resx</DependentUpon>
     </Compile>
     <Compile Include="Services\MesService.cs" />
+    <Compile Include="ViewModels\User\CardLoginWindowViewModel.cs" />
     <Compile Include="ViewModels\Home\ChangeNozzleViewModel.cs" />
     <Compile Include="ViewModels\Home\ChangeHeaderViewModel.cs" />
     <Compile Include="ViewModels\Home\AddScrewViewModel.cs" />
@@ -1083,6 +1084,9 @@
     <Compile Include="Views\UserView.xaml.cs">
       <DependentUpon>UserView.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Views\User\CardLoginWindow.xaml.cs">
+      <DependentUpon>CardLoginWindow.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Views\User\LoginView.xaml.cs">
       <DependentUpon>LoginView.xaml</DependentUpon>
     </Compile>
@@ -1478,6 +1482,10 @@
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
+    <Page Include="Views\User\CardLoginWindow.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="Views\User\LoginView.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>

+ 186 - 0
TeamAAS-VM/ViewModels/User/CardLoginWindowViewModel.cs

@@ -0,0 +1,186 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Prism.Commands;
+using Prism.Events;
+using Prism.Ioc;
+using Prism.Mvvm;
+using Prism.Regions;
+using Prism.Services.Dialogs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Windows;
+using TeamAAS_VP.Core;
+using TeamAAS_VP.Core.Lights;
+using TeamAAS_VP.Interfaces;
+using TeamAAS_VP.Views;
+using TeamAAS_VP.Views.User;
+
+namespace TeamAAS_VP.ViewModels.User
+{
+    public class CardLoginWindowViewModel : BindableBase
+    {
+        #region 字段
+        IRegionManager _regionManager;
+        IRegionNavigationService _regionNavigationService;
+        IContainerProvider _container;
+        IDialogService _dialogService;
+        IEventAggregator _eventAggregator;
+        IConfigService _configService;
+        System.Net.Http.HttpClient httpClient;
+        #endregion
+
+        #region 属性
+
+        private string _Name;
+        public string Name
+        {
+            get { return _Name; }
+            set { SetProperty(ref _Name, value); }
+        }
+
+        private string _Password;
+        public string Password
+        {
+            get { return _Password; }
+            set { SetProperty(ref _Password, value); }
+        }
+
+        public bool IsLocal { get; private set; }
+        public CardLoginWindow View { get; set; }
+
+        //正在登录中
+        private bool _IsLogining;
+        public bool IsLogining
+        {
+            get { return _IsLogining; }
+            set { SetProperty(ref _IsLogining, value); }
+        }
+
+        private int _PageIndex;
+        public int PageIndex
+        {
+            get { return _PageIndex; }
+            set { SetProperty(ref _PageIndex, value); }
+        }
+
+        private Visibility _CardBtnVisibility = Visibility.Visible;
+        public Visibility CardBtnVisibility
+        {
+            get { return _CardBtnVisibility; }
+            set { SetProperty(ref _CardBtnVisibility, value); }
+        }
+
+        public SerialPortProtocol serialPortProtocol { get; set; }
+        #endregion
+
+        #region 命令
+        private DelegateCommand _LoginCommand;
+        public DelegateCommand LoginCommand =>
+            _LoginCommand ?? (_LoginCommand = new DelegateCommand(ExecuteLoginCommand, CanExecuteLoginCommand).ObservesProperty(() => Name).ObservesProperty(() => Password).ObservesProperty(() => IsLogining));
+
+        private DelegateCommand _CloseCommand;
+        public DelegateCommand CloseCommand =>
+            _CloseCommand ?? (_CloseCommand = new DelegateCommand(ExecuteCloseCommand));
+
+        private DelegateCommand<string> _TransitionerCommand;
+        public DelegateCommand<string> TransitionerCommand =>
+            _TransitionerCommand ?? (_TransitionerCommand = new DelegateCommand<string>(ExecuteTransitionerCommand));
+
+        private DelegateCommand _LoadedCommand;
+        public DelegateCommand LoadedCommand =>
+            _LoadedCommand ?? (_LoadedCommand = new DelegateCommand(ExecuteLoadedCommand));
+        #endregion
+
+        #region 事件
+
+        #endregion
+
+        public CardLoginWindowViewModel(IRegionManager regionManager, IEventAggregator ea, IRegionNavigationService regionNavigationService, IContainerProvider container, IDialogService dialogService, IConfigService configService)
+        {
+            _regionManager = regionManager;
+            _regionNavigationService = regionNavigationService;
+            _container = container;
+            _dialogService = dialogService;
+            _configService = configService;
+            _eventAggregator = ea;
+            if (httpClient == null)
+            {
+                httpClient = new System.Net.Http.HttpClient();
+            }
+        }
+
+
+        #region 方法
+        /// <summary>
+        /// 加载
+        /// </summary>
+        async void ExecuteLoadedCommand()
+        {
+            //获取刷卡器配置
+            var cardConfig = _configService.GetCardReaderConfig();
+            //创建刷卡器通讯
+            serialPortProtocol = new SerialPortProtocol(cardConfig);
+            serialPortProtocol.DataReceived += CardSerial_DataReceived;
+            await serialPortProtocol.ConnectAsync();
+        }
+
+        private async void CardSerial_DataReceived(object sender, string code)
+        {
+            if (PageIndex == 0)
+            {
+                if (string.IsNullOrEmpty(code))
+                {
+                    return;
+                }
+               
+            }
+        }
+
+        public void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
+        {
+            serialPortProtocol?.Dispose();
+        }
+
+        async void ExecuteLoginCommand()
+        {
+            IsLogining = true;
+            try
+            {
+                
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show(ex.Message);
+            }
+            finally
+            {
+                IsLogining = false;
+            }
+        }
+
+        bool CanExecuteLoginCommand()
+        {
+            return !string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Password) && !IsLogining;
+        }
+
+        void ExecuteCloseCommand()
+        {
+            View.DialogResult = false;
+        }
+
+        void ExecuteTransitionerCommand(string parameter)
+        {
+            PageIndex = int.Parse(parameter);
+        }
+
+        public void SetModel(bool isAdmin)
+        {
+            PageIndex = isAdmin ? 1 : 0;
+            CardBtnVisibility = isAdmin ? Visibility.Collapsed : Visibility.Visible;
+        }
+        #endregion
+    }
+}

+ 368 - 0
TeamAAS-VM/Views/User/CardLoginWindow.xaml

@@ -0,0 +1,368 @@
+<Window x:Class="TeamAAS_VP.Views.User.CardLoginWindow"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
+        xmlns:prism="http://prismlibrary.com/"
+        xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
+        xmlns:vm="clr-namespace:TeamAAS_VP.ViewModels.User"
+        xmlns:local="clr-namespace:TeamAAS_VP"
+        xmlns:converter="clr-namespace:TeamAAS_VP.ValueConverter"
+        xmlns:lex="http://wpflocalizeextension.codeplex.com"
+        lex:LocalizeDictionary.DesignCulture="zh-CN"
+        lex:ResxLocalizationProvider.DefaultAssembly="TeamAAS-VP"
+        lex:ResxLocalizationProvider.DefaultDictionary="Lang"
+        mc:Ignorable="d"
+        d:DataContext="{d:DesignInstance Type=vm:CardLoginWindowViewModel}"
+        Background="Transparent"
+        WindowState="Normal"
+        WindowStyle="None"
+        WindowStartupLocation="CenterScreen"
+        AllowsTransparency="True"
+        Width="563"
+        Height="561"
+        FontFamily="{DynamicResource DefaultFont}">
+    <b:Interaction.Triggers>
+        <b:EventTrigger EventName="Loaded">
+            <b:InvokeCommandAction Command="{Binding LoadedCommand}" />
+        </b:EventTrigger>
+        <b:EventTrigger EventName="Closing">
+            <b:CallMethodAction MethodName="Window_Closing"
+                                TargetObject="{Binding}" />
+        </b:EventTrigger>
+    </b:Interaction.Triggers>
+    <Border Width="563"
+            Height="561"
+            CornerRadius="60"
+            BorderBrush="Black"
+            BorderThickness="1">
+        <Border.Background>
+            <LinearGradientBrush EndPoint="0.497,1.007"
+                                 StartPoint="0.494,0.009">
+                <GradientStop Color="#FF309EAF" />
+                <GradientStop Color="White"
+                              Offset="1" />
+            </LinearGradientBrush>
+        </Border.Background>
+        <Grid>
+            <Button Content="X"
+                    FontSize="16"
+                    FontWeight="Bold"
+                    Width="32"
+                    Height="32"
+                    HorizontalAlignment="Right"
+                    VerticalAlignment="Top"
+                    Margin="0,20,18,0"
+                    Command="{Binding CloseCommand}"
+                    Background="Transparent"
+                    BorderBrush="Transparent"
+                    Foreground="Black" />
+            <TextBlock Text="用户登录"
+                       HorizontalAlignment="Center"
+                       VerticalAlignment="Top"
+                       FontSize="32"
+                       Margin="0,58,0,0"
+                       FontWeight="Bold" />
+            <materialDesign:Transitioner Margin="20,110,20,20"
+                                         AutoApplyTransitionOrigins="True"
+                                         d:SelectedIndex="0"
+                                         SelectedIndex="{Binding PageIndex}">
+                <materialDesign:TransitionerSlide OpeningEffect="{materialDesign:TransitionEffect FadeIn}">
+                    <Grid Background="Transparent">
+                        <Grid>
+                            <!-- 刷卡动画区域 -->
+                            <Border BorderThickness="1"
+                                    BorderBrush="#bdc3c7"
+                                    Background="#f8f9fa"
+                                    CornerRadius="8"
+                                    Margin="0,10"
+                                    VerticalAlignment="Top"
+                                    HorizontalAlignment="Center"
+                                    Padding="30">
+                                <Canvas Width="300"
+                                        Height="200">
+                                    <!-- 刷卡器主体 -->
+                                    <Rectangle Canvas.Left="50"
+                                               Canvas.Top="70"
+                                               Width="200"
+                                               Height="60"
+                                               Fill="#34495e"
+                                               RadiusX="5"
+                                               RadiusY="5">
+                                        <Rectangle.Effect>
+                                            <DropShadowEffect ShadowDepth="2"
+                                                              Direction="270"
+                                                              BlurRadius="5"
+                                                              Opacity="0.2" />
+                                        </Rectangle.Effect>
+                                    </Rectangle>
+
+                                    <!-- 刷卡槽 -->
+                                    <Rectangle Canvas.Left="60"
+                                               Canvas.Top="75"
+                                               Width="180"
+                                               Height="10"
+                                               Fill="#7f8c8d"
+                                               RadiusX="2"
+                                               RadiusY="2" />
+
+                                    <!-- 动态卡片 -->
+                                    <Border x:Name="AnimatedCard"
+                                            Canvas.Left="50"
+                                            Canvas.Top="10"
+                                            Width="180"
+                                            Height="100"
+                                            Background="#3498db"
+                                            CornerRadius="8"
+                                            BorderThickness="1"
+                                            BorderBrush="#2980b9">
+                                        <Border.Effect>
+                                            <DropShadowEffect ShadowDepth="3"
+                                                              Direction="270"
+                                                              BlurRadius="10"
+                                                              Opacity="0.3" />
+                                        </Border.Effect>
+
+                                        <!-- 卡片动画 -->
+                                        <Border.Triggers>
+                                            <EventTrigger RoutedEvent="Loaded">
+                                                <BeginStoryboard>
+                                                    <Storyboard RepeatBehavior="Forever">
+                                                        <!-- 卡片下落动画 -->
+                                                        <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)"
+                                                                         From="10"
+                                                                         To="70"
+                                                                         Duration="0:0:1"
+                                                                         AutoReverse="True"
+                                                                         BeginTime="0:0:0" />
+
+                                                        <!-- 卡片透明度变化 -->
+                                                        <DoubleAnimation Storyboard.TargetProperty="Opacity"
+                                                                         From="1"
+                                                                         To="0.7"
+                                                                         Duration="0:0:0.5"
+                                                                         AutoReverse="True"
+                                                                         BeginTime="0:0:0.5" />
+                                                    </Storyboard>
+                                                </BeginStoryboard>
+                                            </EventTrigger>
+                                        </Border.Triggers>
+
+                                        <!-- 卡片内部设计 -->
+                                        <Grid>
+                                            <Grid.RowDefinitions>
+                                                <RowDefinition Height="*" />
+                                                <RowDefinition Height="Auto" />
+                                            </Grid.RowDefinitions>
+
+                                            <TextBlock Text="员工卡"
+                                                       Foreground="White"
+                                                       FontSize="14"
+                                                       FontWeight="Bold"
+                                                       HorizontalAlignment="Center"
+                                                       VerticalAlignment="Center" />
+
+                                            <Rectangle Grid.Row="1"
+                                                       Height="20"
+                                                       Fill="#2980b9"
+                                                       RadiusX="0"
+                                                       RadiusY="0,0,8,8" />
+                                            <TextBlock Grid.Row="1"
+                                                       Text="●●●● 1234"
+                                                       Foreground="White"
+                                                       FontSize="10"
+                                                       HorizontalAlignment="Center"
+                                                       VerticalAlignment="Center" />
+                                        </Grid>
+                                    </Border>
+
+                                    <!-- 扫描光线效果 -->
+                                    <Rectangle x:Name="ScanBeam"
+                                               Canvas.Left="60"
+                                               Canvas.Top="75"
+                                               Width="180"
+                                               Height="2"
+                                               Fill="#2ecc71"
+                                               Opacity="0">
+                                        <Rectangle.Effect>
+                                            <BlurEffect Radius="3" />
+                                        </Rectangle.Effect>
+
+                                        <Rectangle.Triggers>
+                                            <EventTrigger RoutedEvent="Loaded">
+                                                <BeginStoryboard>
+                                                    <Storyboard RepeatBehavior="Forever">
+                                                        <!-- 扫描光束移动动画 -->
+                                                        <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)"
+                                                                         From="75"
+                                                                         To="125"
+                                                                         Duration="0:0:0.8"
+                                                                         BeginTime="0:0:0.5" />
+
+                                                        <!-- 扫描光束透明度动画 -->
+                                                        <DoubleAnimation Storyboard.TargetProperty="Opacity"
+                                                                         From="0"
+                                                                         To="1"
+                                                                         Duration="0:0:0.2"
+                                                                         AutoReverse="True"
+                                                                         BeginTime="0:0:0.5" />
+                                                    </Storyboard>
+                                                </BeginStoryboard>
+                                            </EventTrigger>
+                                        </Rectangle.Triggers>
+                                    </Rectangle>
+
+                                    <!-- 状态指示灯 -->
+                                    <Ellipse Canvas.Left="20"
+                                             Canvas.Top="80"
+                                             Width="12"
+                                             Height="12"
+                                             Fill="#e74c3c">
+                                        <Ellipse.Triggers>
+                                            <EventTrigger RoutedEvent="Loaded">
+                                                <BeginStoryboard>
+                                                    <Storyboard RepeatBehavior="Forever">
+                                                        <!-- 指示灯颜色变化动画 -->
+                                                        <ColorAnimation Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)"
+                                                                        From="#e74c3c"
+                                                                        To="#2ecc71"
+                                                                        Duration="0:0:1"
+                                                                        AutoReverse="True"
+                                                                        BeginTime="0:0:0.5" />
+                                                    </Storyboard>
+                                                </BeginStoryboard>
+                                            </EventTrigger>
+                                        </Ellipse.Triggers>
+                                    </Ellipse>
+                                </Canvas>
+                            </Border>
+
+                            <!-- 提示文字 -->
+                            <TextBlock Text="请刷卡..."
+                                       HorizontalAlignment="Center"
+                                       VerticalAlignment="Bottom"
+                                       Margin="0,120"
+                                       FontSize="16"
+                                       Foreground="#2c3e50"
+                                       FontWeight="SemiBold" />
+                        </Grid>
+                        <Button Height="50"
+                                Width="50"
+                                materialDesign:ButtonAssist.CornerRadius="5"
+                                HorizontalAlignment="Center"
+                                VerticalAlignment="Bottom"
+                                Margin="0,0,0,44"
+                                ToolTip="账户登录"
+                                Command="{Binding TransitionerCommand}"
+                                CommandParameter="1"
+                                Padding="0"
+                                IsEnabled="{Binding IsLogining,Converter={StaticResource InvertBooleanConverter}}">
+                            <Button.Content>
+                                <materialDesign:PackIcon Kind="User"
+                                                         Height="32"
+                                                         Width="32" />
+                            </Button.Content>
+                        </Button>
+                    </Grid>
+                </materialDesign:TransitionerSlide>
+                <Grid HorizontalAlignment="Center"
+                      Margin="0,50">
+                    <Grid.RowDefinitions>
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="Auto" />
+
+                        <RowDefinition Height="Auto" />
+                    </Grid.RowDefinitions>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="Auto" />
+                        <ColumnDefinition Width="Auto" />
+                    </Grid.ColumnDefinitions>
+                    <TextBlock Grid.Row="0"
+                               Grid.Column="0"
+                               HorizontalAlignment="Left"
+                               VerticalAlignment="Center"
+                               Margin="20"
+                               FontSize="18"
+                               FontWeight="Bold"
+                               Text="用户名:" />
+                    <TextBox Grid.Row="0"
+                             Grid.Column="1"
+                             HorizontalAlignment="Left"
+                             VerticalAlignment="Center"
+                             Margin="20"
+                             Width="200"
+                             BorderThickness="1,1,1,1"
+                             FontSize="16"
+                             TextAlignment="Center"
+                             Text="{Binding Name}" />
+                    <TextBlock Grid.Row="1"
+                               Grid.Column="0"
+                               HorizontalAlignment="Left"
+                               VerticalAlignment="Center"
+                               Margin="20"
+                               FontSize="18"
+                               FontWeight="Bold"
+                               Text="密码:" />
+                    <PasswordBox Grid.Row="1"
+                                 Grid.Column="1"
+                                 HorizontalAlignment="Left"
+                                 VerticalAlignment="Center"
+                                 Margin="20"
+                                 Width="200"
+                                 HorizontalContentAlignment="Center"
+                                 VerticalContentAlignment="Center"
+                                 BorderThickness="1,1,1,1"
+                                 PasswordChar="*"
+                                 FontSize="16"
+                                 Padding="0,4,0,4"
+                                 materialDesign:HintAssist.HelperText="请输入用户登录密码"
+                                 materialDesign:TextFieldAssist.HasClearButton="True"
+                                 materialDesign:PasswordBoxAssist.Password="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}" />
+                    <Button Grid.Row="2"
+                            Grid.Column="0"
+                            Grid.ColumnSpan="2"
+                            HorizontalAlignment="Center"
+                            VerticalAlignment="Center"
+                            Content="{lex:Loc 登录}"
+                            Height="30"
+                            Margin="0,50"
+                            MinWidth="200"
+                            BorderThickness="0"
+                            materialDesign:ButtonAssist.CornerRadius="5"
+                            Command="{Binding LoginCommand}">
+                        <Button.Background>
+                            <LinearGradientBrush EndPoint="0.5,1"
+                                                 StartPoint="0.5,0">
+                                <GradientStop Color="#FF73B3BD"
+                                              Offset="0" />
+                                <GradientStop Color="#FF309EAF"
+                                              Offset="1" />
+                            </LinearGradientBrush>
+                        </Button.Background>
+                    </Button>
+                    <Button Grid.Row="3"
+                            Grid.Column="0"
+                            Grid.ColumnSpan="2"
+                            Height="50"
+                            Width="50"
+                            materialDesign:ButtonAssist.CornerRadius="5"
+                            HorizontalAlignment="Center"
+                            VerticalAlignment="Center"
+                            ToolTip="刷卡登录"
+                            Padding="0"
+                            Command="{Binding TransitionerCommand}"
+                            CommandParameter="0"
+                            Visibility="{Binding CardBtnVisibility}"
+                            IsEnabled="{Binding IsLogining,Converter={StaticResource InvertBooleanConverter}}">
+                        <Button.Content>
+                            <materialDesign:PackIcon Kind="CreditCardScanOutline" />
+                        </Button.Content>
+                    </Button>
+                </Grid>
+            </materialDesign:Transitioner>
+
+        </Grid>
+    </Border>
+</Window>

+ 20 - 0
TeamAAS-VM/Views/User/CardLoginWindow.xaml.cs

@@ -0,0 +1,20 @@
+using System.Windows;
+
+namespace TeamAAS_VP.Views.User
+{
+    /// <summary>
+    /// Interaction logic for CardLoginWindow.xaml
+    /// </summary>
+    public partial class CardLoginWindow : Window
+    {
+        public CardLoginWindow()
+        {
+            InitializeComponent();
+            Loaded += (sender, e) =>
+            {
+                var vm = DataContext as ViewModels.User.CardLoginWindowViewModel;
+                vm.View = this;
+            };
+        }
+    }
+}