Преглед изворни кода

取消工厂MES屏蔽按钮 自动启动服务

刘彬 пре 3 недеља
родитељ
комит
00a665cb4e

+ 24 - 6
LocalhostMES/Api/Controllers/MesController.cs

@@ -113,7 +113,7 @@ namespace LocalhostMES.Api.Controllers
             try
             {
                 LogHelper.WriteLogInfo("接口getWorkOrders收到消息");
-                var workOrders = DatabaseHelper.SelectWorkOrderInfo(null, management.IsLocalhostMode);
+                var workOrders = DatabaseHelper.SelectWorkOrderInfo(null);
                 var response = new ApiResponse<List<WorkOrderInfo>>
                 {
                     code = "200",
@@ -172,7 +172,7 @@ namespace LocalhostMES.Api.Controllers
 
                 LogHelper.WriteLogInfo("请求参数:");
                 LogHelper.WriteLogInfo(JsonConvert.SerializeObject(request, Formatting.Indented));
-                if ( management.IsLocalhostMode )
+                if ( workOrder[0].IsLocalhost)
                 {
                     var Manualsninfo = DatabaseHelper.SelectSnInfo(request.workOrderNo);
                     DatabaseHelper.InsertSnInfo(new SnInfo()
@@ -324,7 +324,14 @@ namespace LocalhostMES.Api.Controllers
             {
 
                 LogHelper.WriteLogInfo($"收到请求: 接收SN和关键件");
-                if ( management.IsLocalhostMode )
+                var res= DatabaseHelper.SelectWorkOrderInfo();        
+                var workOrder = DatabaseHelper.SelectWorkOrderInfo(res.LeftOrderNo);
+                // 验证工单是否存在
+                if ( workOrder.Count == 0 )
+                {
+                    return BadRequest($"工单 {res.LeftOrderNo} 不存在");
+                }
+                if ( workOrder[0].IsLocalhost )
                 {
                     return Ok(new ApiResponse<bool?>() { data = true });
                 }
@@ -362,7 +369,7 @@ namespace LocalhostMES.Api.Controllers
                             ScanType = request.scanTpye
                         };
                         DatabaseHelper.InsertBindRecord(bindRecord);
-                        if ( management.IsLocalhostMode )
+                        if ( workOrder[ 0 ].IsLocalhost )
                         {
                             //if (DatabaseHelper.GetProductProductionRecords(request.sn) == 0)
                             //{
@@ -396,7 +403,7 @@ namespace LocalhostMES.Api.Controllers
                     DatabaseHelper.InsertBindRecord(bindRecord);
 
                 }
-                if ( management.IsLocalhostMode )
+                if ( workOrder[ 0 ].IsLocalhost )
                 {
                     LogHelper.WriteLogInfo($"收到请求: 接收SN和关键件  成功");
                     return Ok(new ApiResponse<bool?>() { data = true });
@@ -470,6 +477,17 @@ namespace LocalhostMES.Api.Controllers
             try
             {
                 LogHelper.WriteLogInfo($"收到请求:接收工件加工参数 ");
+
+                var res= DatabaseHelper.SelectWorkOrderInfo();
+
+
+                var workOrder = DatabaseHelper.SelectWorkOrderInfo(res.LeftOrderNo);
+                // 验证工单是否存在
+                if ( workOrder.Count == 0 )
+                {
+                    return BadRequest($"工单 {res.LeftOrderNo} 不存在");
+                }
+
                 // 验证请求头
                 if ( !ValidateHeaders() )
                 {
@@ -502,7 +520,7 @@ namespace LocalhostMES.Api.Controllers
                 };
 
                 DatabaseHelper.InsertProcessRecord(processRecord);
-                if ( management.IsLocalhostMode )
+                if ( workOrder[ 0 ].IsLocalhost )
                 {
                     var response = new ApiResponse<bool>
                     {

+ 17 - 3
LocalhostMES/App.xaml.cs

@@ -7,6 +7,7 @@ using LocalhostMES.Views;
 using Prism.Ioc;
 using Prism.Unity;
 using System;
+using System.Diagnostics;
 using System.Windows;
 
 namespace LocalhostMES
@@ -19,9 +20,6 @@ namespace LocalhostMES
 
         protected override Window CreateShell()
         {
-
-       
-
             // 写入启动日志
             LogHelper.WriteLogInfo("程序启动");
             return Container.Resolve<MainView>();
@@ -46,6 +44,22 @@ namespace LocalhostMES
         {
             base.OnInitialized();
             ThemeManager.ApplyTheme(ThemeMode.Light);
+            AutoRunSystemSettings();
+        }
+
+        private void AutoRunSystemSettings()
+        {
+            try
+            {
+                var systemSettingsViewModel = Container.Resolve<SystemSettingsViewModel>();
+                systemSettingsViewModel.SaveSettings();
+                systemSettingsViewModel.StartService();
+                Process.Start(@"C:\Windows\System32\notepad.exe")
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show( $"启动自动初始化系统设置失败: {ex.Message}");
+            }
         }
         private static System.Threading.Mutex mutex;
         protected override void OnStartup(StartupEventArgs e)

+ 6 - 5
LocalhostMES/Core/Management.cs

@@ -18,16 +18,11 @@ namespace LocalhostMES.Core
 
         private  WebApiService _webApiService;
         private MesApiClient _apiClient;
-        private WorkOrderNotifyTcpClient _workOrderNotifyTcpClient;
         private List<WorkOrderInfo> _getOrderInfo = new List<WorkOrderInfo>();
 
-        private bool _IsLocalhostMode = false;
-
         public MesApiClient ApiClient { get => _apiClient; set => _apiClient = value; }
         public WebApiService WebApiService { get => _webApiService; set => _webApiService = value; }
-        public WorkOrderNotifyTcpClient WorkOrderNotifyTcpClient { get => _workOrderNotifyTcpClient; set => _workOrderNotifyTcpClient = value; }
         public List<WorkOrderInfo> GetOrderInfo { get => _getOrderInfo; set => _getOrderInfo = value; }
-        public bool IsLocalhostMode { get => _IsLocalhostMode; set => _IsLocalhostMode = value; }
 
         public static Management management = new Management();
 
@@ -36,5 +31,11 @@ namespace LocalhostMES.Core
             return management;
         }
 
+        public Management() 
+        {
+        
+        
+        }
+
     }
 }

+ 0 - 88
LocalhostMES/Core/WorkOrderNotifyTcpClient.cs

@@ -1,88 +0,0 @@
-using System;
-using System.Net;
-using System.Text;
-using System.Threading.Tasks;
-using TouchSocket.Core;
-using TouchSocket.Sockets;
-
-namespace LocalhostMES.Core
-{
-    public class WorkOrderNotifyTcpClient : IDisposable
-    {
-        private readonly TcpClient _tcpClient;
-        private readonly string _host;
-        private readonly int _port;
-
-        public event Action<string> MessageReceived;
-        public event Action<string> LogReceived;
-
-        public WorkOrderNotifyTcpClient(string host, int port)
-        {
-            _host = host;
-            _port = port;
-            _tcpClient = new TcpClient();
-
-            var config = new TouchSocketConfig();
-            config.SetRemoteIPHost(new IPHost(IPAddress.Parse(_host), _port));
-            config.SetTcpDataHandlingAdapter(() => new NormalDataHandlingAdapter());
-            config.ConfigurePlugins(a => { a.UseTcpReconnection(); });
-
-            _tcpClient.Setup(config);
-            _tcpClient.Connected = OnConnected;
-            _tcpClient.Closed = OnClosed;
-            _tcpClient.Received = OnReceived;
-        }
-
-        public bool IsConnected => _tcpClient.Online;
-
-        public async Task ConnectAsync()
-        {
-            if (_tcpClient.Online)
-            {
-                return;
-            }
-
-            await _tcpClient.ConnectAsync();
-        }
-
-        public void Disconnect()
-        {
-            if (_tcpClient.Online)
-            {
-                _tcpClient.Close();
-            }
-        }
-
-        private Task OnConnected(ITcpClient client, ConnectedEventArgs e)
-        {
-            LogReceived?.Invoke($"TCP客户端已连接 {_host}:{_port}");
-            return EasyTask.CompletedTask;
-        }
-
-        private Task OnClosed(ITcpClient client, ClosedEventArgs e)
-        {
-            LogReceived?.Invoke("TCP客户端已断开");
-            return EasyTask.CompletedTask;
-        }
-
-        private Task OnReceived(ITcpClient client, ReceivedDataEventArgs e)
-        {
-            var text = e.ByteBlock.Span.ToString(Encoding.UTF8);
-            LogReceived?.Invoke($"TCP收到消息: {text}");
-            MessageReceived?.Invoke(text);
-            return EasyTask.CompletedTask;
-        }
-
-        public void Dispose()
-        {
-            try
-            {
-                Disconnect();
-            }
-            catch
-            {
-            }
-            _tcpClient.Dispose();
-        }
-    }
-}

+ 55 - 2
LocalhostMES/DataBase/DatabaseHelper.cs

@@ -91,7 +91,7 @@ namespace LocalhostMES.DataBase
             }
             return 0;
         }
-        public static List<WorkOrderInfo> SelectWorkOrderInfo(string workOrderNo = null,bool islocalhost=false)
+        public static List<WorkOrderInfo> SelectWorkOrderInfo(string workOrderNo = null)
         {
             if ( workOrderNo != null )
             {
@@ -444,7 +444,60 @@ namespace LocalhostMES.DataBase
 
             return ok;
         }
-        
 
+        #region 当前左右工位识别码
+
+        public static OrderToMesInfo SelectWorkOrderInfo()
+        {
+            List<OrderToMesInfo> lilsts = Db.Queryable<OrderToMesInfo>().ToList();
+            if ( lilsts.Count != 0 )
+            {
+                return lilsts[ 0 ];
+            }
+            else
+            {
+                return new OrderToMesInfo() { CreateTime = DateTime.Now, Id = 0, LeftCheckCode = "", RightCheckCode = "" };
+            }
+        }
+
+        public static bool InsertWorkOrderInfo(OrderToMesInfo info)
+        {
+
+            if ( Db.Storageable(info).ExecuteCommand() == 1 )
+            {
+                return true;
+            }
+            return false;
+        }
+
+        public static bool UpdateWorkOrderInfo(OrderToMesInfo info, bool isleft = true)
+        {
+            if ( info.CurrMarkCode != string.Empty )
+            {
+
+                if ( Db.Updateable(info).UpdateColumns(it => new { it.CurrMarkCode }).Where(it => it.Id == info.Id).ExecuteCommand() == 1 )
+                {
+                    return true;
+                }
+                return false;
+            }
+            if ( isleft )
+            {
+                if ( Db.Updateable(info).UpdateColumns(it => new { it.CreateTime, it.LeftCheckCode }).Where(it => it.Id == info.Id).ExecuteCommand() == 1 )
+                {
+                    return true;
+                }
+            }
+            else
+            {
+                if ( Db.Updateable(info).UpdateColumns(it => new { it.CreateTime, it.RightCheckCode }).Where(it => it.Id == info.Id).ExecuteCommand() == 1 )
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        #endregion
     }
 }

+ 1 - 2
LocalhostMES/LocalhostMES.csproj

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
@@ -423,7 +423,6 @@
     <Compile Include="Core\MesDataChangedEvent.cs" />
     <Compile Include="Core\ThemeManager.cs" />
     <Compile Include="Core\Management.cs" />
-    <Compile Include="Core\WorkOrderNotifyTcpClient.cs" />
     <Compile Include="Core\TrayIconManager.cs" />
     <Compile Include="DataBase\DatabaseHelper.cs" />
     <Compile Include="Enums\FormulaPartType.cs" />

+ 27 - 0
LocalhostMES/Models/MesModel.cs

@@ -1,5 +1,6 @@
 using LocalhostMES.Enums;
 using Prism.Mvvm;
+using SqlSugar;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -335,5 +336,31 @@ namespace LocalhostMES.Models
         public string ResultValue { get => resultValue; set { SetProperty(ref resultValue, value); } }
         public string Result { get => result; set { SetProperty(ref result, value); } }
     }
+
+
+    /// <summary>
+    /// 正在工作的订单左右信息 下发
+    /// </summary>
+    public class OrderToMesInfo
+    {
+        private int _id=1;
+        private string _LeftOrderNo;
+        private string _LeftCheckCode;
+        private string _RightOrderNo;
+        private string _RightCheckCode;
+        private DateTime _CreateTime;
+        private string _CurrMarkCode=string.Empty;
+
+        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
+        public int Id { get => _id; set => _id = value; }
+
+        public string LeftOrderNo { get => _LeftOrderNo; set => _LeftOrderNo = value; }
+        public string RightOrderNo { get => _RightOrderNo; set => _RightOrderNo = value; }
+        public string LeftCheckCode { get => _LeftCheckCode; set => _LeftCheckCode = value; }
+        public string RightCheckCode { get => _RightCheckCode; set => _RightCheckCode = value; }
+
+        public string CurrMarkCode { get => _CurrMarkCode; set => _CurrMarkCode = value; }
+        public DateTime CreateTime { get => _CreateTime; set => _CreateTime = value; }
+    }
     #endregion
 }

+ 3 - 82
LocalhostMES/ViewModels/Services/MesWorkspace.cs

@@ -1,6 +1,7 @@
 using LocalhostMES.Core;
 using LocalhostMES.DataBase;
 using LocalhostMES.Models;
+using LocalhostMES.ViewModels.Tabs;
 using Prism.Mvvm;
 using System;
 using System.Collections.Generic;
@@ -61,7 +62,6 @@ namespace LocalhostMES.ViewModels.Services
             _initialized = true;
             DatabaseHelper.CreataDataTable();
             ReloadWorkOrders();
-            EnsureOrderNotifyClient();
 
             _logHandler = msg =>
             {
@@ -75,14 +75,7 @@ namespace LocalhostMES.ViewModels.Services
 
         public void Shutdown()
         {
-            if (MesManagement.WorkOrderNotifyTcpClient != null)
-            {
-                MesManagement.WorkOrderNotifyTcpClient.MessageReceived -= OnOrderNotifyMessage;
-                MesManagement.WorkOrderNotifyTcpClient.LogReceived -= OnOrderNotifyLog;
-                MesManagement.WorkOrderNotifyTcpClient.Dispose();
-                MesManagement.WorkOrderNotifyTcpClient = null;
-            }
-
+           
             if (_logHandler != null)
             {
                 LogHelper.logChangHandler -= _logHandler;
@@ -93,7 +86,7 @@ namespace LocalhostMES.ViewModels.Services
         public void ReloadWorkOrders()
         {
             WorkOrders.Clear();
-            foreach (var w in DatabaseHelper.SelectWorkOrderInfo())
+            foreach (var w in DatabaseHelper.SelectWorkOrderInfo(null))
             {
                 WorkOrders.Add(w);
             }
@@ -108,30 +101,6 @@ namespace LocalhostMES.ViewModels.Services
             });
         }
 
-        private void EnsureOrderNotifyClient()
-        {
-            if (MesManagement.WorkOrderNotifyTcpClient != null)
-            {
-                return;
-            }
-
-            var client = new WorkOrderNotifyTcpClient("127.0.0.1", 9000);
-            client.MessageReceived += OnOrderNotifyMessage;
-            client.LogReceived += OnOrderNotifyLog;
-            MesManagement.WorkOrderNotifyTcpClient = client;
-
-            Task.Run(async () =>
-            {
-                try
-                {
-                    await client.ConnectAsync();
-                }
-                catch (Exception ex)
-                {
-                    ShowStatus($"TCP客户端连接失败: {ex.Message}", true);
-                }
-            });
-        }
 
         private void OnOrderNotifyLog(string message)
         {
@@ -141,53 +110,5 @@ namespace LocalhostMES.ViewModels.Services
             });
         }
 
-        private async void OnOrderNotifyMessage(string message)
-        {
-            if (string.IsNullOrWhiteSpace(message))
-            {
-                return;
-            }
-
-            if (!message.StartsWith("OrderRef|", StringComparison.OrdinalIgnoreCase))
-            {
-                return;
-            }
-
-            try
-            {
-                var httpHelper = MesManagement.ApiClient;
-                if (httpHelper == null)
-                {
-                    ShowStatus("未配置API客户端,无法自动刷新工单", true);
-                    return;
-                }
-
-                ApiResponse<List<WorkOrderInfo>> apiResponse = await httpHelper.GetWorkOrdersAsync();
-                if (!apiResponse.success)
-                {
-                    return;
-                }
-                if (apiResponse.data == null || apiResponse.data.Count == 0)
-                {
-                    return;
-                }
-
-                MesManagement.GetOrderInfo = apiResponse.data;
-
-                foreach (var item in apiResponse.data.Where(it => !string.IsNullOrWhiteSpace(it.WorkOrderNo)))
-                {
-                    if (!DatabaseHelper.UpdateWorkOrderInfo(item))
-                    {
-                        DatabaseHelper.InsertWorkOrderInfo(item);
-                    }
-                }
-
-                ReloadWorkOrders();
-                ShowStatus($"收到{message},工单已自动刷新", false);
-            }
-            catch
-            {
-            }
-        }
     }
 }

+ 2 - 2
LocalhostMES/ViewModels/Tabs/SystemSettingsViewModel.cs

@@ -109,7 +109,7 @@ namespace LocalhostMES.ViewModels.Tabs
         public DelegateCommand SaveSettingsCommand =>
             _saveSettingsCommand ?? (_saveSettingsCommand = new DelegateCommand(SaveSettings));
 
-        private async void StartService()
+        public async void StartService()
         {
             try
             {
@@ -160,7 +160,7 @@ namespace LocalhostMES.ViewModels.Tabs
             }
         }
 
-        private void SaveSettings()
+        public void SaveSettings()
         {
             try
             {

+ 2 - 2
LocalhostMES/ViewModels/Tabs/TcpServerViewModel.cs

@@ -236,7 +236,7 @@ namespace LocalhostMES.ViewModels.Tabs
             var newWorkOrders = new List<string>();
             lock (_workOrderSync)
             {
-                var latest = DatabaseHelper.SelectWorkOrderInfo();
+                var latest = DatabaseHelper.SelectWorkOrderInfo(null);
                 var latestNos = new HashSet<string>(
                     latest.Where(w => !string.IsNullOrWhiteSpace(w.WorkOrderNo)).Select(w => w.WorkOrderNo),
                     StringComparer.OrdinalIgnoreCase);
@@ -286,7 +286,7 @@ namespace LocalhostMES.ViewModels.Tabs
         {
             lock (_workOrderSync)
             {
-                var latest = DatabaseHelper.SelectWorkOrderInfo();
+                var latest = DatabaseHelper.SelectWorkOrderInfo(null);
                 _knownWorkOrderNos = new HashSet<string>(
                     latest.Where(w => !string.IsNullOrWhiteSpace(w.WorkOrderNo)).Select(w => w.WorkOrderNo),
                     StringComparer.OrdinalIgnoreCase);

+ 1 - 3
LocalhostMES/Views/MainView.xaml.cs

@@ -41,8 +41,6 @@ namespace LocalhostMES.Views
         {
             try
             {
-
-
                 // 初始化托盘图标
                 trayIconManager = new TrayIconManager();
                 trayIconManager.OnExit += OnExit;
@@ -58,7 +56,7 @@ namespace LocalhostMES.Views
 
         private void OnSettings(object sender, EventArgs e)
         {
-            this.WindowState = WindowState.Normal;
+            this.WindowState = WindowState.Maximized;
             this.Show();
         }
 

+ 86 - 33
LocalhostMES/Views/Tabs/SystemSettingsView.xaml

@@ -7,7 +7,8 @@
              xmlns:prism="http://prismlibrary.com/"
              prism:ViewModelLocator.AutoWireViewModel="True"
              mc:Ignorable="d"
-             d:DesignHeight="480" d:DesignWidth="780">
+             d:DesignHeight="480"
+             d:DesignWidth="780">
     <Grid Margin="10">
         <b:Interaction.Triggers>
             <b:EventTrigger EventName="Loaded">
@@ -22,55 +23,107 @@
             <RowDefinition Height="Auto" />
             <RowDefinition Height="*" />
         </Grid.RowDefinitions>
-        <GroupBox Grid.Row="0" Header="Web API服务设置">
+        <GroupBox Grid.Row="0"
+                  Header="Web API服务设置">
             <StackPanel>
-                <StackPanel Orientation="Horizontal" Margin="0,5">
-                    <TextBlock Text="服务端口:" Width="80" VerticalAlignment="Center" />
-                    <TextBox Width="100" Text="{Binding ServerPort}" />
-                    <Button Content="启动服务" Style="{StaticResource SuccessButtonStyle}" Width="90" IsEnabled="{Binding StartServiceEnable}" Command="{Binding StartServiceCommand}" />
-                    <Button Content="停止服务" Style="{StaticResource DangerButtonStyle}" Width="90" IsEnabled="{Binding StopServiceEnable}" Command="{Binding StopServiceCommand}" />
-                    <CheckBox Content="屏蔽远程MES" Width="120" IsEnabled="{Binding StopServiceEnable}" Margin="24,0,0,0" IsChecked="{Binding Management.IsLocalhostMode}" />
+                <StackPanel Orientation="Horizontal"
+                            Margin="0,5">
+                    <TextBlock Text="服务端口:"
+                               Width="80"
+                               VerticalAlignment="Center" />
+                    <TextBox Width="100"
+                             Text="{Binding ServerPort}" />
+                    <Button Content="启动服务"
+                            Style="{StaticResource SuccessButtonStyle}"
+                            Width="90"
+                            IsEnabled="{Binding StartServiceEnable}"
+                            Command="{Binding StartServiceCommand}" />
+                    <Button Content="停止服务"
+                            Style="{StaticResource DangerButtonStyle}"
+                            Width="90"
+                            IsEnabled="{Binding StopServiceEnable}"
+                            Command="{Binding StopServiceCommand}" />
                 </StackPanel>
-                <TextBlock Foreground="{Binding Servicebrushes}" Text="{Binding ServiceStatus}" Margin="0,5" />
+                <TextBlock Foreground="{Binding Servicebrushes}"
+                           Text="{Binding ServiceStatus}"
+                           Margin="0,5" />
             </StackPanel>
         </GroupBox>
-        <GroupBox Grid.Row="1" Header="API客户端设置" Margin="0,12,0,0">
+        <GroupBox Grid.Row="1"
+                  Header="API客户端设置"
+                  Margin="0,12,0,0">
             <StackPanel>
-                <StackPanel Orientation="Horizontal" Margin="0,5">
-                    <TextBlock Text="API地址:" Width="80" VerticalAlignment="Center" />
-                    <TextBox Width="320" Text="{Binding ApiUrl}" />
+                <StackPanel Orientation="Horizontal"
+                            Margin="0,5">
+                    <TextBlock Text="API地址:"
+                               Width="80"
+                               VerticalAlignment="Center" />
+                    <TextBox Width="320"
+                             Text="{Binding ApiUrl}" />
                 </StackPanel>
-                <StackPanel Orientation="Horizontal" Margin="0,5">
-                    <TextBlock Text="App Key:" Width="80" VerticalAlignment="Center" />
-                    <TextBox Width="220" Text="{Binding AppKey}" />
+                <StackPanel Orientation="Horizontal"
+                            Margin="0,5">
+                    <TextBlock Text="App Key:"
+                               Width="80"
+                               VerticalAlignment="Center" />
+                    <TextBox Width="220"
+                             Text="{Binding AppKey}" />
                 </StackPanel>
-                <StackPanel Orientation="Horizontal" Margin="0,5">
-                    <TextBlock Text="Token:" Width="80" VerticalAlignment="Center" />
-                    <TextBox Width="220" Text="{Binding Token}" />
+                <StackPanel Orientation="Horizontal"
+                            Margin="0,5">
+                    <TextBlock Text="Token:"
+                               Width="80"
+                               VerticalAlignment="Center" />
+                    <TextBox Width="220"
+                             Text="{Binding Token}" />
                 </StackPanel>
-                <Button Content="保存设置" Style="{StaticResource SecondaryButtonStyle}" Width="90" Margin="0,10,0,0" Command="{Binding SaveSettingsCommand}" />
+                <Button Content="保存设置"
+                        Style="{StaticResource SecondaryButtonStyle}"
+                        Width="90"
+                        Margin="0,10,0,0"
+                        Command="{Binding SaveSettingsCommand}" />
             </StackPanel>
         </GroupBox>
-        <GroupBox Grid.Row="2" Header="外观主题" Margin="0,12,0,0">
+        <GroupBox Grid.Row="2"
+                  Header="外观主题"
+                  Margin="0,12,0,0">
             <StackPanel Orientation="Horizontal">
-                <TextBlock Text="深色模式:" Width="80" VerticalAlignment="Center" />
-                <CheckBox IsChecked="{Binding IsDarkTheme}" VerticalAlignment="Center" />
-                <TextBlock Text="(切换后立即生效)" Margin="8,0,0,0" VerticalAlignment="Center" Opacity="0.8" />
+                <TextBlock Text="深色模式:"
+                           Width="80"
+                           VerticalAlignment="Center" />
+                <CheckBox IsChecked="{Binding IsDarkTheme}"
+                          VerticalAlignment="Center" />
+                <TextBlock Text="(切换后立即生效)"
+                           Margin="8,0,0,0"
+                           VerticalAlignment="Center"
+                           Opacity="0.8" />
             </StackPanel>
         </GroupBox>
-        <GroupBox Grid.Row="3" Header="接口地址" Margin="0,12,0,0">
+        <GroupBox Grid.Row="3"
+                  Header="接口地址"
+                  Margin="0,12,0,0">
             <StackPanel>
-                <TextBlock Text="1. 工单下发接口: /api/mes/receiveWorkOrder" Margin="0,2" />
-                <TextBlock Text="2. SN打印请求接口: /api/mes/requestSnPrint" Margin="0,2" />
-                <TextBlock Text="3. SN关键件绑定接口: /api/mes/receiveSnComponent" Margin="0,2" />
-                <TextBlock Text="4. 加工参数上报接口: /api/mes/receiveProcessParameters" Margin="0,2" />
+                <TextBlock Text="1. 工单下发接口: /api/mes/receiveWorkOrder"
+                           Margin="0,2" />
+                <TextBlock Text="2. SN打印请求接口: /api/mes/requestSnPrint"
+                           Margin="0,2" />
+                <TextBlock Text="3. SN关键件绑定接口: /api/mes/receiveSnComponent"
+                           Margin="0,2" />
+                <TextBlock Text="4. 加工参数上报接口: /api/mes/receiveProcessParameters"
+                           Margin="0,2" />
             </StackPanel>
         </GroupBox>
-        <GroupBox Grid.Row="4" Header="工单状态说明" Margin="0,12,0,0">
+        <GroupBox Grid.Row="4"
+                  Header="工单状态说明"
+                  Margin="0,12,0,0">
             <StackPanel>
-                <TextBlock Text="0:已创建、1:已排产、2:已发布、3:关闭、4:已锁定、5:已开工、6:已完成、7:异常完工" Margin="0,2" TextWrapping="Wrap" />
-                <TextBlock Text="冻结状态: 0:否、1:是" Margin="0,2" />
-                <TextBlock Text="下发状态: 0:未下发、1:已下发" Margin="0,2" />
+                <TextBlock Text="0:已创建、1:已排产、2:已发布、3:关闭、4:已锁定、5:已开工、6:已完成、7:异常完工"
+                           Margin="0,2"
+                           TextWrapping="Wrap" />
+                <TextBlock Text="冻结状态: 0:否、1:是"
+                           Margin="0,2" />
+                <TextBlock Text="下发状态: 0:未下发、1:已下发"
+                           Margin="0,2" />
             </StackPanel>
         </GroupBox>
     </Grid>