From a3ba25607612818c28d421bfa8eb2f92339d71ac Mon Sep 17 00:00:00 2001
From: alielie <alielie@git.com>
Date: 星期四, 13 三月 2025 20:30:48 +0800
Subject: [PATCH] 优化项目;

---
 HMI.Core/Util/PathHelper.cs                    |    2 
 HMI.ParameterManage/SettingModal.xaml          |   81 ++
 HMI.ParameterManage/ParameterSetting.xaml      |   90 +
 HMI.ParameterManage/SettingModal.xaml.cs       |  112 +++
 HMI.UI/Style/FormStyle.xaml                    |    1 
 HMI.Util/Properties/AssemblyInfo.cs            |   33 +
 WPFProject/WPFProject.csproj                   |    3 
 HMI.ParameterManage/Entity/ParameterData.cs    |   11 
 WPFProject/Lang/zh-CN/app.json                 |    4 
 HMI.ParameterManage/HMI.ParameterManage.csproj |   35 +
 HMI.ParameterManage/ParameterSetting.xaml.cs   |  215 ++++++
 HMI.ParameterManage/Util/TestItemManager.cs    |   44 +
 WPFProject/TextFile1.txt                       |   21 
 HMI.UI/Style/PanuonResource.xaml               |    4 
 HMI.ParameterManage/TestItemModal.xaml         |   71 ++
 WPFProject/WPFProject.sln                      |   10 
 HMI.ParameterManage/Entity/TestItemData.cs     |   67 ++
 HMI.MenuManage/Modal/IconSelector.xaml.cs      |    2 
 HMI.ParameterManage/TestItem.xaml              |  168 +++++
 HMI.Util/packages.config                       |    5 
 HMI.Util/ExcelColumn.cs                        |   19 
 Plugin.TestResult/ViewControl.xaml.cs          |    2 
 HMI.Util/ExcelHelper.cs                        |  137 ++++
 HMI.DataViewer/DataViewer.xaml.cs              |    2 
 HMI.ParameterManage/TestItemModal.xaml.cs      |  114 +++
 HMI.ParameterManage/Util/ParameterManager.cs   |   49 +
 HMI.MenuManage/Modal/MenuModal.xaml.cs         |    2 
 WPFProject/Lang/zh-CN/ParamManage.json         |   15 
 HMI.Util/PropertyHelper.cs                     |   30 
 Plugin.TsExecution/TsExecution.xaml.cs         |    6 
 HMI.UI/Util/ToastHelper.cs                     |   24 
 HMI.Util/HMI.Util.csproj                       |   69 ++
 /dev/null                                      |    1 
 HMI.ParameterManage/TestItem.xaml.cs           |  327 ++++++++++
 HMI.UI/Util/MsgBoxHelper.cs                    |    4 
 HMI.Util/IniFileHelper.cs                      |  112 +++
 36 files changed, 1,832 insertions(+), 60 deletions(-)

diff --git a/HMI.Core/Util/PathHelper.cs b/HMI.Core/Util/PathHelper.cs
index 50f2b09..0d67354 100644
--- a/HMI.Core/Util/PathHelper.cs
+++ b/HMI.Core/Util/PathHelper.cs
@@ -100,7 +100,7 @@
             return Path;
         }
 
-        private static string InitFolder(string path)
+        public static string InitFolder(string path)
         {
             try
             {
diff --git a/HMI.DataViewer/DataViewer.xaml.cs b/HMI.DataViewer/DataViewer.xaml.cs
index e6dafc7..a1e2400 100644
--- a/HMI.DataViewer/DataViewer.xaml.cs
+++ b/HMI.DataViewer/DataViewer.xaml.cs
@@ -431,7 +431,7 @@
                 workbook.CreateSheet("sheet1");////鏂板缓涓�涓伐浣滆〃
                 XSSFSheet SheetOne = (XSSFSheet)workbook.GetSheet("Sheet1"); ////鑾峰彇鍚嶇О涓篠heet1鐨勫伐浣滆〃,鍒涘缓鏍囬澶�
                 IRow row1 = SheetOne.CreateRow(0);////鑾峰彇Sheet1宸ヤ綔琛ㄧ殑棣栬,璁剧疆琛ㄥご锛岃幏鍙栧垪
-                XSSFCell[] SheetCell = new XSSFCell[currentColumns.Count];
+                //XSSFCell[] SheetCell = new XSSFCell[currentColumns.Count];
                 for (int i = 0; i < currentColumns.Count; i++)
                 {
                     row1.CreateCell(i).SetCellValue(currentColumns[i].Header.ToString());
diff --git a/HMI.MenuManage/Modal/IconSelector.xaml.cs b/HMI.MenuManage/Modal/IconSelector.xaml.cs
index 1411ece..4d18399 100644
--- a/HMI.MenuManage/Modal/IconSelector.xaml.cs
+++ b/HMI.MenuManage/Modal/IconSelector.xaml.cs
@@ -71,7 +71,7 @@
 
         public bool? Open(string currentValue)
         {
-            list.ItemsSource = icons.Select(i => i.Code).ToList();
+            list.ItemsSource = icons?.Select(i => i.Code).ToList();
             Data = currentValue;
             return this.ShowDialog();
         }
diff --git a/HMI.MenuManage/Modal/MenuModal.xaml.cs b/HMI.MenuManage/Modal/MenuModal.xaml.cs
index ef3f9c3..a961195 100644
--- a/HMI.MenuManage/Modal/MenuModal.xaml.cs
+++ b/HMI.MenuManage/Modal/MenuModal.xaml.cs
@@ -85,7 +85,7 @@
             string sql = $"SELECT 1 FROM menu WHERE code='{TbCode.Text}'{segment};";
             if (sqliteHelper.HasData(sql))
             {
-                err = $"{LangUtil.Get("HMI.Menu.Code")}{LangUtil.Get("HMI.Menu.CodeMustBeUnique")}";
+                err = this.lang("App.MustBeUnique", LangUtil.Get("HMI.Menu.Code")); ;
                 return false;
             }
             return true;
diff --git a/HMI.ParameterManage/Entity/ParameterData.cs b/HMI.ParameterManage/Entity/ParameterData.cs
index c0d5e2b..a4b9cd7 100644
--- a/HMI.ParameterManage/Entity/ParameterData.cs
+++ b/HMI.ParameterManage/Entity/ParameterData.cs
@@ -1,15 +1,26 @@
 锘縰sing HMI.Core.Entity;
+using HMI.Util;
 
 namespace HMI.ParameterManage.Entity
 {
     public class ParameterData : SelectBase
     {
         public string Id { get; set; }
+        [ExcelColumn("璁剧疆椤瑰悕绉�")]
         public string Name { get; set; }
+        [ExcelColumn("璁剧疆椤圭被鍨�")]
+        public string Type { get; set; }
+        [ExcelColumn("鍊�")]
         public string Value { get; set; }
+        [ExcelColumn("鍗曚綅")]
         public string Unit { get; set; }
+        [ExcelColumn("鎻忚堪")]
         public string Description { get; set; }
+        [ExcelColumn("鍒嗙粍")]
         public string PGroup { get; set; }
+        [ExcelColumn("鏉ユ簮")]
+        public string Source { get; set; }
+
         public string ProductName { get; set; }
     }
 }
diff --git a/HMI.ParameterManage/Entity/TestItemData.cs b/HMI.ParameterManage/Entity/TestItemData.cs
new file mode 100644
index 0000000..3f0dbc6
--- /dev/null
+++ b/HMI.ParameterManage/Entity/TestItemData.cs
@@ -0,0 +1,67 @@
+锘縰sing HMI.Core.Entity;
+using HMI.Util;
+
+namespace HMI.ParameterManage.Entity
+{
+    public class TestItemData : SelectBase
+    {
+        /// <summary>
+        /// 鍙傛暟ID
+        /// </summary>
+        public string ID { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 娴嬭瘯椤瑰悕绉�
+        /// </summary>
+        [ExcelColumn("娴嬭瘯椤瑰悕绉�")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 娴嬭瘯椤圭被鍨�
+        /// </summary>
+        [ExcelColumn("娴嬭瘯椤圭被鍨�")]
+        public string Type { get; set; } = "NumberTest";
+
+        /// <summary>
+        /// 瀛楃涓插瀷鏁版嵁
+        /// </summary>
+        [ExcelColumn("瀛楃涓�")]
+        public string StrValue { get; set; }
+
+
+        /// <summary>
+        /// 娴嬭瘯椤逛笅闄�
+        /// </summary>
+        [ExcelColumn("涓嬮檺")]
+        public double LimitDown { get; set; }
+
+        /// <summary>
+        /// 娴嬭瘯椤逛笂闄�
+        /// </summary>
+        [ExcelColumn("涓婇檺")]
+        public double LimitUp { get; set; }
+
+        /// <summary>
+        /// 浣胯兘
+        /// </summary>
+        [ExcelColumn("浣胯兘")]
+        public bool Enable { get; set; }
+
+        /// <summary>
+        /// 琛ュ伩鍊�
+        /// </summary>
+        [ExcelColumn("琛ュ伩鍊�")]
+        public double Offset { get; set; }
+
+        /// <summary>
+        /// 鏉ユ簮
+        /// </summary>
+        [ExcelColumn("鏉ユ簮")]
+        public string Source { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 鍒嗙粍
+        /// </summary>
+        public string Group { get; set; } = string.Empty;
+    }
+}
diff --git a/HMI.ParameterManage/HMI.ParameterManage.csproj b/HMI.ParameterManage/HMI.ParameterManage.csproj
index 9b0274b..f8c2d82 100644
--- a/HMI.ParameterManage/HMI.ParameterManage.csproj
+++ b/HMI.ParameterManage/HMI.ParameterManage.csproj
@@ -53,6 +53,7 @@
     <Reference Include="log4net">
       <HintPath>..\WPFProject\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
     </Reference>
+    <Reference Include="NPOI, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1" />
     <Reference Include="Panuon.WPF">
       <HintPath>..\WPFProject\packages\Panuon.WPF.1.1.2.2\lib\net472\Panuon.WPF.dll</HintPath>
     </Reference>
@@ -75,14 +76,36 @@
     <Reference Include="PresentationFramework" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Entity\TestItemData.cs" />
+    <Compile Include="TestItemModal.xaml.cs">
+      <DependentUpon>TestItemModal.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="TestItem.xaml.cs">
+      <DependentUpon>TestItem.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="SettingModal.xaml.cs">
+      <DependentUpon>SettingModal.xaml</DependentUpon>
+    </Compile>
     <Compile Include="ParameterModal.xaml.cs">
       <DependentUpon>ParameterModal.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="ParameterSetting.xaml.cs">
+      <DependentUpon>ParameterSetting.xaml</DependentUpon>
     </Compile>
     <Compile Include="ProductModal.xaml.cs">
       <DependentUpon>ProductModal.xaml</DependentUpon>
     </Compile>
     <Compile Include="Model\XmlModel.cs" />
+    <Compile Include="Util\TestItemManager.cs" />
     <Compile Include="Util\ParameterManager.cs" />
+    <Page Include="TestItemModal.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="TestItem.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
     <Page Include="ParameterManage.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
@@ -94,7 +117,15 @@
       <DependentUpon>ParameterManage.xaml</DependentUpon>
       <SubType>Code</SubType>
     </Compile>
+    <Page Include="SettingModal.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
     <Page Include="ParameterModal.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="ParameterSetting.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
@@ -139,6 +170,10 @@
       <Project>{f17de6b5-af1d-48c6-94a0-6b763a6546a6}</Project>
       <Name>HMI.UI</Name>
     </ProjectReference>
+    <ProjectReference Include="..\HMI.Util\HMI.Util.csproj">
+      <Project>{afea100a-9223-448d-ab10-3015d93dd98c}</Project>
+      <Name>HMI.Util</Name>
+    </ProjectReference>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <PropertyGroup>
diff --git a/HMI.ParameterManage/ParameterSetting.xaml b/HMI.ParameterManage/ParameterSetting.xaml
index 2a102e2..c4e41b5 100644
--- a/HMI.ParameterManage/ParameterSetting.xaml
+++ b/HMI.ParameterManage/ParameterSetting.xaml
@@ -15,7 +15,7 @@
              Loaded="UserControl_Loaded"
              Background="{StaticResource WorkspaceBackground}"
              d:DesignHeight="450" d:DesignWidth="800">
-    
+
     <Border Style="{StaticResource CardBoarder}">
         <Grid dp:GridHelper.Space="10" Cursor="">
             <Grid.RowDefinitions>
@@ -33,6 +33,8 @@
                             x:Name="addBtn" Click="Button_Click"/>
                     <Button Style="{StaticResource RfhBtn}"
                             x:Name="refreshBtn" Click="Button_Click"/>
+                    <Button Style="{StaticResource DelBtn}" 
+                            x:Name="delBatchBtn" Click="Button_Click"/>
                     <Button x:Name="exportBtn"
                             Style="{StaticResource ExportBtn}" Click="Button_Click"/>
                     <Button x:Name="importBtn" 
@@ -42,40 +44,22 @@
                 <StackPanel Style="{StaticResource ButtonGroup}" HorizontalAlignment="Left">
 
                     <TextBlock Style="{StaticResource TitleBox}" FontSize="14" Text="{DynamicResource HMI.Line}"/>
-                    <ComboBox Style="{StaticResource FormComboxStyle}" x:Name="CbLine"/>
+                    <ComboBox Style="{StaticResource FormComboxStyle}" Width="160" x:Name="CbLine"/>
 
                     <TextBlock Style="{StaticResource TitleBox}" FontSize="14" Text="{DynamicResource HMI.Station}"/>
-                    <ComboBox Style="{StaticResource FormComboxStyle}" x:Name="CbStation"/>
+                    <ComboBox Style="{StaticResource FormComboxStyle}" Width="160" x:Name="CbStation"/>
                 </StackPanel>
             </DockPanel>
 
 
-            <DataGrid Grid.Row="2" x:Name="table" Style="{StaticResource DefaultDataGrid}">
-                <DataGrid.RowStyle>
-                    <Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridRowStyle}}">
-                        <Style.Triggers>
-                            <DataTrigger Binding="{Binding type}" Value="warn">
-                                <Setter Property="Background" Value="{StaticResource WarningColor}"/>
-                                <Setter Property="Foreground" Value="Black"/>
-                            </DataTrigger>
-                            <DataTrigger Binding="{Binding type}" Value="error">
-                                <Setter Property="Background" Value="{StaticResource DangerColor}"/>
-                                <Setter Property="Foreground" Value="Black"/>
-                            </DataTrigger>
-                            <Trigger Property="IsSelected" Value="True">
-                                <Setter Property="Background" Value="Transparent"/>
-                            </Trigger>
-                            <Trigger Property="IsMouseOver" Value="True">
-                                <Setter Property="Background" Value="Transparent"/>
-                            </Trigger>
-                        </Style.Triggers>
-                    </Style>
-                </DataGrid.RowStyle>
+            <DataGrid Grid.Row="2" x:Name="table" Style="{StaticResource DefaultDataGrid}"
+                      ItemsSource="{Binding Datasource}">
                 <DataGrid.Columns>
                     <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
                         <DataGridTemplateColumn.CellTemplate>
                             <DataTemplate>
-                                <CheckBox VerticalAlignment="Center" IsChecked="{Binding Selected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
+                                <CheckBox VerticalAlignment="Center" 
+                                          IsChecked="{Binding IsSelected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                             </DataTemplate>
                         </DataGridTemplateColumn.CellTemplate>
                     </DataGridTemplateColumn>
@@ -87,10 +71,58 @@
                             </DataTemplate>
                         </DataGridTemplateColumn.CellTemplate>
                     </DataGridTemplateColumn>
-                    <DataGridTextColumn Width="*" Binding="{Binding type}" Header="绾у埆" />
-                    <DataGridTextColumn Width="*" Binding="{Binding time}" Header="鏃堕棿" />
-                    <DataGridTextColumn Width="*" Binding="{Binding source}" Header="鏉ユ簮" />
-                    <DataGridTextColumn Width="*" Binding="{Binding text}" Header="鏂囨湰" />
+                    <DataGridTextColumn Width="*" Binding="{Binding Name}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Pm.Parameter.Name}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTextColumn Width="*" Binding="{Binding Type}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Pm.Parameter.Type}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTextColumn Width="*" Binding="{Binding Value}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Pm.Parameter.Value}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTextColumn Width="*" Binding="{Binding Description}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Pm.Parameter.Desc}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTextColumn Width="*" Binding="{Binding Unit}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Pm.Parameter.Unit}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTextColumn Width="*" Binding="{Binding PGroup}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Pm.Parameter.Group}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTextColumn Width="*" Binding="{Binding Source}">
+                        <DataGridTextColumn.Header>
+                            <TextBlock Text="{DynamicResource Pm.Parameter.Source}"/>
+                        </DataGridTextColumn.Header>
+                    </DataGridTextColumn>
+                    <DataGridTemplateColumn Width="110" IsReadOnly="True" CanUserResize="False">
+                        <DataGridTemplateColumn.Header>
+                            <TextBlock Text="{DynamicResource App.Opt}"/>
+                        </DataGridTemplateColumn.Header>
+                        <DataGridTemplateColumn.CellTemplate>
+                            <DataTemplate>
+                                <StackPanel Orientation="Horizontal">
+                                    <Button x:Name="editBtn" Style="{StaticResource EditLink}"
+                                            Click="Button_Click" Margin="0,0,10,0" 
+                                            DataContext="{Binding }"/>
+                                    <Button x:Name="delBtn" Style="{StaticResource DeleteLink}"
+                                            DataContext="{Binding }"
+                                            Click="Button_Click"/>
+                                </StackPanel>
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellTemplate>
+                    </DataGridTemplateColumn>
                 </DataGrid.Columns>
             </DataGrid>
         </Grid>
diff --git a/HMI.ParameterManage/ParameterSetting.xaml.cs b/HMI.ParameterManage/ParameterSetting.xaml.cs
index 8b8c0a0..7db5211 100644
--- a/HMI.ParameterManage/ParameterSetting.xaml.cs
+++ b/HMI.ParameterManage/ParameterSetting.xaml.cs
@@ -1,11 +1,17 @@
 锘縰sing HMI.Base;
 using HMI.Core.Util;
+using HMI.ParameterManage.Entity;
+using HMI.ParameterManage.Util;
 using HMI.UI.Util;
+using HMI.Util;
+using Microsoft.Win32;
 using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.Linq;
 using System.Windows;
 using System.Windows.Controls;
+using System.Windows.Markup;
 
 namespace HMI.ParameterManage
 {
@@ -15,9 +21,18 @@
     public partial class ParameterSetting : PluginControl
     {
 
+        private ObservableCollection<ParameterData> datasource;
+        public ObservableCollection<ParameterData> Datasource
+        {
+            get => datasource;
+            set
+            {
+                Set(ref datasource, value);
+            }
+        }
+
         public ParameterSetting()
         {
-           
             this.DataContext = this;
             InitializeComponent();
         }
@@ -25,11 +40,207 @@
         private void Button_Click(object sender, RoutedEventArgs e)
         {
             var btn = ((Button)sender);
-            
+            ParameterData data = btn.DataContext as ParameterData;
+            switch (btn.Name)
+            {
+                case "addBtn":
+                    OpenDataModal(null, false);
+                    break;
+                case "editBtn":
+                    OpenDataModal(data, true);
+                    break;
+                case "refreshBtn":
+                    LoadData();
+                    break;
+                case "delBatchBtn":
+                    DeleteBatch();
+                    break;
+                case "exportBtn":
+                    ExportData();
+                    break;
+                case "importBtn":
+                    ImportData();
+                    break;
+                case "delBtn":
+                    DeleteData(data);
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// 鍔犺浇鏁版嵁
+        /// </summary>
+        public void LoadData()
+        {
+            try
+            {
+                Datasource = ParameterManager.GetCommonParameter<ObservableCollection<ParameterData>>()
+                ?? new ObservableCollection<ParameterData>();
+            }
+            catch (Exception ex)
+            {
+                Datasource = new ObservableCollection<ParameterData>();
+                log.Error("Refresh Data Failed.", ex);
+                ToastHelper.Error(Window.GetWindow(this), $"{this.lang("App.RefreshFailed")}: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鎵撳紑鏂板鎴栫紪杈戠獥鍙�
+        /// </summary>
+        /// <param name="data"></param>
+        public void OpenDataModal(ParameterData data, bool isUpdate)
+        {
+            try
+            {
+                int index = isUpdate ? Datasource.IndexOf(data) : -1;
+                string title = isUpdate ? $"{this.lang("App.Edit")}" : $"{this.lang("App.Add")}";
+                SettingModal modal = new SettingModal()
+                {
+                    Title = title,
+                    Owner = Window.GetWindow(this)
+                };
+                if (modal.Open(data ?? new ParameterData(), Datasource) == true)
+                {
+                    if (index == -1)
+                    {
+                        Datasource.Add(modal.Data);
+                        ParameterManager.SaveCommonParameter(Datasource);
+                    }
+                    else
+                    {
+                        ParameterManager.SaveCommonParameter(Datasource);
+                    }
+                    LoadData();
+                }
+            }
+            catch (Exception ex)
+            {
+                if (isUpdate)
+                {
+                    log.Error("Edit Data Failed.", ex);
+                    MsgBoxHelper.Error(Window.GetWindow(this), $"{this.lang("App.EditFailed")}: {ex.Message}");
+                }
+                else
+                {
+                    log.Error("Add Data Failed.", ex);
+                    MsgBoxHelper.Error(Window.GetWindow(this), $"{this.lang("App.AddFailed")}: {ex.Message}");
+                }
+            }
+        }
+
+        /// <summary>
+        /// 鎵归噺鍒犻櫎
+        /// </summary>
+        public void DeleteBatch()
+        {
+            try
+            {
+                var count = Datasource.Where(d => d.IsSelected).Count();
+                if (count <= 0)
+                {
+                    ToastHelper.Warning(this, this.lang("HMI.NoDeleteData"));
+                    return;
+                }
+                if (MsgBoxHelper.WarnMessage(this, this.lang("HMI.MakeSureDeletes", count))
+                    == MessageBoxResult.OK)
+                {
+                    var temp = Datasource.Where(d => !d.IsSelected).ToList();
+                    ParameterManager.SaveCommonParameter(temp);
+                    LoadData();
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("Delete Batch Data Failed.", ex);
+                ToastHelper.Error(Window.GetWindow(this), $"{this.lang("App.DeleteFailed")}: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鍒犻櫎鍙傛暟
+        /// </summary>
+        /// <param name="data"></param>
+        private void DeleteData(ParameterData data)
+        {
+            try
+            {
+                if (MsgBoxHelper.WarnMessage(this, this.lang("HMI.MakeSureDeleteSingle", data.Name))
+                    == MessageBoxResult.OK)
+                {
+                    int index = Datasource.IndexOf(data);
+                    if (index < 0)
+                    {
+                        ToastHelper.Error(this, this.lang("App.ParameterException"));
+                        return;
+                    }
+                    var temp = XmlHelper.DeepClone(Datasource);
+                    temp.RemoveAt(index);
+                    ParameterManager.SaveCommonParameter(temp);
+                    LoadData();
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("Delete Parameter Failed.", ex);
+                ToastHelper.Error(this, $"{this.lang("App.DeleteFailed")}: {ex.Message}");
+                return;
+            }
+        }
+
+        /// <summary>
+        /// 瀵煎嚭鏁版嵁
+        /// </summary>
+        private void ExportData()
+        {
+            try
+            {
+                SaveFileDialog sfd = new SaveFileDialog();
+
+                sfd.Filter = "";
+                sfd.DefaultExt = "xls";
+                sfd.FileName = this.lang("Pm.Setting.Title");
+                if (sfd.ShowDialog() == true)
+                {
+                    string targetPath = sfd.FileName;
+                    ExcelHelper.ExportExcel(targetPath, Datasource);
+                    ToastHelper.Success(Window.GetWindow(this), this.lang("App.ExportSuccess"));
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("Export Data Failed.", ex);
+                MsgBoxHelper.Error(Window.GetWindow(this), $"{this.lang("App.ExportFailed")}: {ex.Message}");
+            }
+        }
+
+        private void ImportData()
+        {
+            try
+            {
+                OpenFileDialog openFileDialog = new OpenFileDialog();
+                openFileDialog.Filter = "execl(*.xlsx;*.xls)|*.xlsx;*.xls";
+                openFileDialog.Title = this.lang("App.SelectFile");
+                openFileDialog.Multiselect = false;
+                if (openFileDialog.ShowDialog() == true)
+                {
+                    List<ParameterData> datas = ExcelHelper.ReadFromExcel<ParameterData>(openFileDialog.FileName);
+                    ParameterManager.SaveCommonParameter(datas);
+                    LoadData();
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("Export Data Failed.", ex);
+                MsgBoxHelper.Error(Window.GetWindow(this), $"{this.lang("App.ExportFailed")}: {ex.Message}");
+            }
         }
 
         private void UserControl_Loaded(object sender, RoutedEventArgs e)
         {
+            LoadData();
             Loaded -= UserControl_Loaded;
         }
 
diff --git a/HMI.ParameterManage/SettingModal.xaml b/HMI.ParameterManage/SettingModal.xaml
new file mode 100644
index 0000000..97ead45
--- /dev/null
+++ b/HMI.ParameterManage/SettingModal.xaml
@@ -0,0 +1,81 @@
+锘�<pu:WindowX x:Class="HMI.ParameterManage.SettingModal"
+            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:local="clr-namespace:HMI.ParameterManage"
+            xmlns:pw="https://opensource.panuon.com/wpf"
+            xmlns:pu="https://opensource.panuon.com/wpf-ui"
+            xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
+            xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
+            xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
+            mc:Ignorable="d"
+            Title="Form"
+            SizeToContent="Height"
+            Width="600"
+            WindowStartupLocation="CenterOwner"
+            Style="{StaticResource ModalWindow}"
+            pu:WindowXModalDialog.OKButtonContent="{DynamicResource App.Save}">
+    <Grid Margin="10">
+        <StackPanel x:Name="formPanel">
+
+            <pu:FormGroup x:Name="FgName"
+                          dp:FormGroupHelper.ValidateMethod="ValidateName"
+                          Header="{DynamicResource Pm.Parameter.Name}" 
+                          dp:FormGroupHelper.IsRequired="True"  GroupName="MyFormGroup">
+                <TextBox x:Name="TbName" Style="{StaticResource EditorTextBoxStyle}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgType"
+              Header="{DynamicResource Pm.Parameter.Type}" 
+              dp:FormGroupHelper.IsRequired="True" GroupName="MyFormGroup">
+                <ComboBox x:Name="CbType" Style="{StaticResource FormComboxStyle}" SelectedValuePath="Content">
+                    <ComboBoxItem>Int</ComboBoxItem>
+                    <ComboBoxItem>Double</ComboBoxItem>
+                    <ComboBoxItem>Bool</ComboBoxItem>
+                    <ComboBoxItem>String</ComboBoxItem>
+                </ComboBox>
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgValue" Style="{StaticResource RequiredInputFormGroupStyle}" 
+                          Header="{DynamicResource Pm.Parameter.Value}" 
+dp:FormGroupHelper.IsRequired="True" GroupName="MyFormGroup">
+                <TextBox x:Name="TbValue" Style="{StaticResource EditorTextBoxStyle}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgUnit" Style="{StaticResource InputFormGroupStyle}" 
+      Header="{DynamicResource Pm.Parameter.Unit}" 
+dp:FormGroupHelper.IsRequired="False" GroupName="MyFormGroup">
+                <TextBox x:Name="TbUnit" Style="{StaticResource EditorTextBoxStyle}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgDescription" Style="{StaticResource InputFormGroupStyle}" 
+Header="{DynamicResource Pm.Parameter.Desc}" 
+dp:FormGroupHelper.IsRequired="False" GroupName="MyFormGroup">
+                <TextBox x:Name="TbDescription" Style="{StaticResource MultilineEditorTextBoxStyle}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgGroup" Style="{StaticResource InputFormGroupStyle}"
+                          Header="{DynamicResource Pm.Parameter.Group}" 
+                          GroupName="MyFormGroup">
+                <ComboBox x:Name="CbGroup" Style="{StaticResource FormComboxStyle}" SelectedValuePath="Content">
+                    <ComboBoxItem>Int</ComboBoxItem>
+                    <ComboBoxItem>Double</ComboBoxItem>
+                    <ComboBoxItem>Bool</ComboBoxItem>
+                    <ComboBoxItem>String</ComboBoxItem>
+                </ComboBox>
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgSource" Style="{StaticResource InputFormGroupStyle}" 
+Header="{DynamicResource Pm.Parameter.Source}" 
+dp:FormGroupHelper.IsRequired="False" GroupName="MyFormGroup">
+                <ComboBox x:Name="CbSource" Style="{StaticResource FormComboxStyle}" SelectedValuePath="Content">
+                    <ComboBoxItem>Int</ComboBoxItem>
+                    <ComboBoxItem>Double</ComboBoxItem>
+                    <ComboBoxItem>Bool</ComboBoxItem>
+                    <ComboBoxItem>String</ComboBoxItem>
+                </ComboBox>
+            </pu:FormGroup>
+        </StackPanel>
+    </Grid>
+</pu:WindowX>
diff --git a/HMI.ParameterManage/SettingModal.xaml.cs b/HMI.ParameterManage/SettingModal.xaml.cs
new file mode 100644
index 0000000..4c19f6b
--- /dev/null
+++ b/HMI.ParameterManage/SettingModal.xaml.cs
@@ -0,0 +1,112 @@
+锘縰sing HMI.Core.Util;
+using HMI.ParameterManage.Entity;
+using HMI.UI.DpUtil;
+using HMI.UI.Util;
+using Panuon.WPF.UI;
+using System;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+
+namespace HMI.ParameterManage
+{
+    /// <summary>
+    /// ModalWindow.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class SettingModal : WindowX
+    {
+        public SettingModal()
+        {
+            this.DataContext = this;
+            InitializeComponent();
+        }
+
+        public ParameterData Data { get; set; }
+        private ObservableCollection<ParameterData> Datasource;
+
+        private bool _isProcessing;
+
+        protected override void OnClosing(CancelEventArgs e)
+        {
+            //Press Continue Button
+            if (DialogResult == true)
+            {
+                if (_isProcessing)
+                {
+                    e.Cancel = true;
+                    return;
+                }
+                var okButton = WindowXModalDialog.GetOKButton(this);
+                try
+                {
+                    _isProcessing = true;
+                    ButtonHelper.SetIsPending(okButton, true);
+                    if (!ValidateForm())
+                    {
+                        e.Cancel = true;
+                        return;
+                    }
+
+                    SaveOrUpdateParameter(Data);
+                }
+                catch (Exception ex)
+                {
+                    ToastHelper.Error(this, $"Error: {ex.Message}");
+                    e.Cancel = true;
+                    return;
+                }
+                finally
+                {
+                    _isProcessing = false;
+                    ButtonHelper.SetIsPending(okButton, false);
+                }
+            }
+            base.OnClosing(e);
+        }
+
+        public bool ValidateName(object value, out string err)
+        {
+            err = null;
+            if (Datasource?.FirstOrDefault(e => e.Name == value.ToString()) != null)
+            {
+                err = this.lang("App.MustBeUnique", this.lang("Pm.Parameter.Name"));
+                return false;
+            }
+            return true;
+        }
+
+        private void SaveOrUpdateParameter(ParameterData data)
+        {
+        }
+
+        private bool ValidateForm()
+        {
+            bool flag = FormGroupHelper.ValidateForm(formPanel);
+            if (flag)
+            {
+                Data.Name = TbName.Text;
+                Data.Type = CbType.Text;
+                Data.Value = TbValue.Text;
+                Data.Unit = TbUnit.Text;
+                Data.Description = TbDescription.Text;
+                Data.PGroup = CbGroup.Text;
+                Data.Source = CbSource.Text;
+            }
+            return flag;
+        }
+
+        public bool? Open(ParameterData data, ObservableCollection<ParameterData> datasource)
+        {
+            Datasource = datasource;
+            Data = data;
+            TbName.Text = Data.Name;
+            CbType.SelectedValue = Data.Type;
+            TbValue.Text = Data.Value;
+            TbUnit.Text = Data.Unit;
+            TbDescription.Text = Data.Description;
+            CbGroup.SelectedValue = Data.PGroup;
+            CbSource.SelectedValue = Data.Source;
+            return this.ShowDialog();
+        }
+    }
+}
diff --git a/HMI.ParameterManage/TestItem.xaml b/HMI.ParameterManage/TestItem.xaml
new file mode 100644
index 0000000..efa1607
--- /dev/null
+++ b/HMI.ParameterManage/TestItem.xaml
@@ -0,0 +1,168 @@
+锘�<hb:PluginControl x:Class="HMI.ParameterManage.TestItem"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
+             xmlns:local="clr-namespace:HMI.ParameterManage"
+             mc:Ignorable="d" 
+             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
+             xmlns:pw="https://opensource.panuon.com/wpf"
+             xmlns:pu="https://opensource.panuon.com/wpf-ui"
+             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
+             Background="{StaticResource WorkspaceBackground}"
+             Loaded="PluginControl_Loaded"
+             d:DesignHeight="450" d:DesignWidth="800">
+    <Border Style="{StaticResource CardBoarder}">
+        <Grid dp:GridHelper.Space="10">
+            <Grid.RowDefinitions>
+                <RowDefinition Height="Auto"/>
+                <RowDefinition Height="Auto"/>
+                <RowDefinition/>
+            </Grid.RowDefinitions>
+
+            <DockPanel Grid.ColumnSpan="2">
+
+                <TextBlock Style="{StaticResource TitleBox}" Text="{DynamicResource Pm.Ti.Title}"/>
+            </DockPanel>
+
+            <DockPanel Grid.Row="1" Cursor="">
+                <StackPanel DockPanel.Dock="Right" Style="{StaticResource ButtonGroup}">
+                    <Button Style="{StaticResource AddBtn}"
+                x:Name="addBtn" Click="Button_Click"/>
+                    <Button Style="{StaticResource RfhBtn}"
+                x:Name="refreshBtn" Click="Button_Click"/>
+                    <Button Style="{StaticResource DelBtn}" 
+                x:Name="delBatchBtn" Click="Button_Click"/>
+                    <Button x:Name="exportBtn"
+                Style="{StaticResource ExportBtn}" Click="Button_Click"/>
+                    <Button x:Name="importBtn" 
+                Style="{StaticResource ImportBtn}" Click="Button_Click"/>
+                </StackPanel>
+
+                <StackPanel Style="{StaticResource ButtonGroup}" HorizontalAlignment="Left">
+
+                    <TextBlock Style="{StaticResource TitleBox}" FontSize="{StaticResource ContentFontSize2}" 
+            Text="{DynamicResource HMI.Line}"/>
+                    <ComboBox x:Name="CbLine" Style="{StaticResource FormComboxStyle}" SelectedValuePath="Content" MinWidth="180"/>
+
+                    <TextBlock Style="{StaticResource TitleBox}" FontSize="{StaticResource ContentFontSize2}" 
+            Text="{DynamicResource HMI.Station}"/>
+                    <ComboBox x:Name="CbStation" Style="{StaticResource FormComboxStyle}" SelectedValuePath="Content" MinWidth="180"/>
+
+                    <TextBlock Style="{StaticResource TitleBox}" FontSize="{StaticResource ContentFontSize2}" 
+            Text="{DynamicResource App.EndTime}"/>
+                    <ComboBox x:Name="CbShineng" Style="{StaticResource FormComboxStyle}" SelectedValuePath="Content" MinWidth="180"/>
+                </StackPanel>
+            </DockPanel>
+
+            <Grid Grid.Row="2">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="2*"/>
+                    <ColumnDefinition Width="8*"/>
+                </Grid.ColumnDefinitions>
+                <Border Grid.Column="0" BorderThickness="2,2,0,2" BorderBrush="{StaticResource CommonBoarder}">
+                    <DataGrid Style="{StaticResource DefaultDataGrid}" 
+                          ItemsSource="{Binding Products}"
+                          SelectedItem="{Binding SelectedProduct}">
+                        <DataGrid.Columns>
+                            <DataGridTemplateColumn Header="#" CanUserResize="False">
+                                <DataGridTemplateColumn.CellTemplate>
+                                    <DataTemplate>
+                                        <TextBlock Text="{Binding Converter={StaticResource rowNumber},
+RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
+                                    </DataTemplate>
+                                </DataGridTemplateColumn.CellTemplate>
+                            </DataGridTemplateColumn>
+                            <DataGridTextColumn Width="*" Binding="{Binding Name}">
+                                <DataGridTextColumn.Header>
+                                    <TextBlock Text="{DynamicResource Pm.Product.Name}"/>
+                                </DataGridTextColumn.Header>
+                            </DataGridTextColumn>
+                        </DataGrid.Columns>
+                    </DataGrid>
+                </Border>
+                <Border Grid.Column="1" BorderThickness="2" BorderBrush="{StaticResource CommonBoarder}">
+                    <DataGrid Style="{StaticResource DefaultDataGrid}" ItemsSource="{Binding TestItems}">
+                        <DataGrid.Columns>
+                            <DataGridTemplateColumn Header="" Width="Auto" CanUserResize="False">
+                                <DataGridTemplateColumn.CellTemplate>
+                                    <DataTemplate>
+                                        <CheckBox VerticalAlignment="Center" 
+                      IsChecked="{Binding IsSelected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
+                                    </DataTemplate>
+                                </DataGridTemplateColumn.CellTemplate>
+                            </DataGridTemplateColumn>
+                            <DataGridTemplateColumn Header="#" CanUserResize="False">
+                                <DataGridTemplateColumn.CellTemplate>
+                                    <DataTemplate>
+                                        <TextBlock Text="{Binding Converter={StaticResource rowNumber},
+RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow},AncestorLevel=1}}"/>
+                                    </DataTemplate>
+                                </DataGridTemplateColumn.CellTemplate>
+                            </DataGridTemplateColumn>
+                            <DataGridTextColumn Width="*" Binding="{Binding Name}">
+                                <DataGridTextColumn.Header>
+                                    <TextBlock Text="{DynamicResource Pm.Ti.Name}"/>
+                                </DataGridTextColumn.Header>
+                            </DataGridTextColumn>
+                            <DataGridTextColumn Width="*" Binding="{Binding Type}">
+                                <DataGridTextColumn.Header>
+                                    <TextBlock Text="{DynamicResource Pm.Ti.Type}"/>
+                                </DataGridTextColumn.Header>
+                            </DataGridTextColumn>
+                            <DataGridTextColumn Width="*" Binding="{Binding Enable}">
+                                <DataGridTextColumn.Header>
+                                    <TextBlock Text="{DynamicResource Pm.Ti.Shineng}"/>
+                                </DataGridTextColumn.Header>
+                            </DataGridTextColumn>
+                            <DataGridTextColumn Width="*" Binding="{Binding LimitDown}">
+                                <DataGridTextColumn.Header>
+                                    <TextBlock Text="{DynamicResource Pm.Ti.LimitDown}"/>
+                                </DataGridTextColumn.Header>
+                            </DataGridTextColumn>
+                            <DataGridTextColumn Width="*" Binding="{Binding LimitUp}">
+                                <DataGridTextColumn.Header>
+                                    <TextBlock Text="{DynamicResource Pm.Ti.LimitUp}"/>
+                                </DataGridTextColumn.Header>
+                            </DataGridTextColumn>
+                            <DataGridTextColumn Width="*" Binding="{Binding StrValue}">
+                                <DataGridTextColumn.Header>
+                                    <TextBlock Text="{DynamicResource Pm.Ti.StrValue}"/>
+                                </DataGridTextColumn.Header>
+                            </DataGridTextColumn>
+                            <DataGridTextColumn Width="*" Binding="{Binding Offset}">
+                                <DataGridTextColumn.Header>
+                                    <TextBlock Text="{DynamicResource Pm.Ti.Offset}"/>
+                                </DataGridTextColumn.Header>
+                            </DataGridTextColumn>
+                            <DataGridTextColumn Width="*" Binding="{Binding Source}">
+                                <DataGridTextColumn.Header>
+                                    <TextBlock Text="{DynamicResource Pm.Ti.Source}"/>
+                                </DataGridTextColumn.Header>
+                            </DataGridTextColumn>
+                            <DataGridTemplateColumn Width="100" IsReadOnly="True" CanUserResize="False">
+                                <DataGridTemplateColumn.Header>
+                                    <TextBlock Text="{DynamicResource App.Opt}"/>
+                                </DataGridTemplateColumn.Header>
+                                <DataGridTemplateColumn.CellTemplate>
+                                    <DataTemplate>
+                                        <StackPanel Orientation="Horizontal">
+                                            <Button x:Name="editBtn" 
+                                                Style="{StaticResource EditLink}" Margin="0,0,10,0"
+          DataContext="{Binding }"
+          Click="Button_Click"/>
+                                            <Button x:Name="delBtn" Style="{StaticResource DeleteLink}"
+          DataContext="{Binding }"
+          Click="Button_Click"/>
+                                        </StackPanel>
+                                    </DataTemplate>
+                                </DataGridTemplateColumn.CellTemplate>
+                            </DataGridTemplateColumn>
+                        </DataGrid.Columns>
+                    </DataGrid>
+                </Border>
+            </Grid>
+        </Grid>
+    </Border>
+</hb:PluginControl>
diff --git a/HMI.ParameterManage/TestItem.xaml.cs b/HMI.ParameterManage/TestItem.xaml.cs
new file mode 100644
index 0000000..96e0570
--- /dev/null
+++ b/HMI.ParameterManage/TestItem.xaml.cs
@@ -0,0 +1,327 @@
+锘縰sing HMI.Base;
+using HMI.Core.Util;
+using HMI.ParameterManage.Entity;
+using HMI.ParameterManage.Util;
+using HMI.UI.Util;
+using HMI.Util;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Windows;
+
+namespace HMI.ParameterManage
+{
+    /// <summary>
+    /// UserControl1.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class TestItem : PluginControl
+    {
+        public TestItem()
+        {
+            DataContext = this;
+            InitializeComponent();
+        }
+
+        private ObservableCollection<ProductData> products;
+        public ObservableCollection<ProductData> Products
+        {
+            get => products;
+            set { Set(ref products, value); }
+        }
+
+        private ObservableCollection<TestItemData> testItems;
+        public ObservableCollection<TestItemData> TestItems
+        {
+            get => testItems;
+            set { Set(ref testItems, value); }
+        }
+
+        private ProductData selectedProduct;
+        public ProductData SelectedProduct
+        {
+            get => selectedProduct;
+            set
+            {
+                Set(ref selectedProduct, value);
+                LoadTestItems();
+            }
+        }
+
+        bool isActivated;
+        public static string FileFolder { get; private set; }
+        public override object Active()
+        {
+            if (!isActivated)
+            {
+                string folder = Path.Combine(ParameterManage.FileFolder, "TestItem");
+                PathHelper.InitFolder(folder);
+                base.SetBaseFolder(folder);
+                FileFolder = folder;
+                isActivated = true;
+            }
+            return null;
+        }
+
+        private void Button_Click(object sender, RoutedEventArgs e)
+        {
+            System.Windows.Controls.Button btn = sender as System.Windows.Controls.Button;
+            TestItemData data = btn.DataContext as TestItemData;
+            switch (btn.Name)
+            {
+                case "addBtn":
+                    OpenTestItemModal(null, false);
+                    break;
+                case "editBtn":
+                    OpenTestItemModal(data, true);
+                    break;
+                case "refreshBtn":
+                    LoadProducts();
+                    break;
+                case "delBtn":
+                    DeleteData(data);
+                    break;
+                case "delBatchBtn":
+                    DeleteBatch();
+                    break;
+                case "exportBtn":
+                    ExportData();
+                    break;
+                case "importBtn":
+                    ImportData();
+                    break;
+                default:
+                    break;
+            }
+        }
+        /// <summary>
+        /// 鍔犺浇鏁版嵁
+        /// </summary>
+        public void LoadTestItems()
+        {
+            if (SelectedProduct == null)
+            {
+                TestItems = new ObservableCollection<TestItemData>();
+                return;
+            }
+            try
+            {
+                TestItems =
+                    TestItemManager.GetTestItemByProduct<ObservableCollection<TestItemData>>(SelectedProduct.Name)
+                ?? new ObservableCollection<TestItemData>();
+            }
+            catch (Exception ex)
+            {
+                TestItems = new ObservableCollection<TestItemData>();
+                log.Error("Refresh Data Failed.", ex);
+                ToastHelper.Error(Window.GetWindow(this), $"{this.lang("App.RefreshFailed")}: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鎵撳紑鏂板鎴栫紪杈戠獥鍙�
+        /// </summary>
+        /// <param name="data"></param>
+        public void OpenTestItemModal(TestItemData data, bool isUpdate)
+        {
+            try
+            {
+                if (SelectedProduct == null)
+                {
+                    ToastHelper.Warning(this, this.lang("Pm.Ti.PleaseSelectProduct"));
+                    return;
+                }
+
+                int index = isUpdate ? TestItems.IndexOf(data) : -1;
+                string title = isUpdate ? $"{this.lang("App.Edit")}" : $"{this.lang("App.Add")}";
+                TestItemModal modal = new TestItemModal()
+                {
+                    Title = title,
+                    Owner = Window.GetWindow(this)
+                };
+                if (modal.Open(data ?? new TestItemData(), TestItems) == true)
+                {
+                    if (index == -1)
+                    {
+                        TestItems.Add(modal.Data);
+                        TestItemManager.SaveTestItemByProduct(SelectedProduct.Name, TestItems);
+                    }
+                    else
+                    {
+                        TestItemManager.SaveTestItemByProduct(SelectedProduct.Name, TestItems);
+                    }
+                    LoadTestItems();
+                }
+            }
+            catch (Exception ex)
+            {
+                if (isUpdate)
+                {
+                    log.Error("Edit Test Item Failed.", ex);
+                    MsgBoxHelper.Error(Window.GetWindow(this), $"{this.lang("App.EditFailed")}: {ex.Message}");
+                }
+                else
+                {
+                    log.Error("Add Test Item Failed.", ex);
+                    MsgBoxHelper.Error(Window.GetWindow(this), $"{this.lang("App.AddFailed")}: {ex.Message}");
+                }
+            }
+        }
+
+        /// <summary>
+        /// 鎵归噺鍒犻櫎
+        /// </summary>
+        public void DeleteBatch()
+        {
+            try
+            {
+                if (SelectedProduct == null)
+                {
+                    ToastHelper.Warning(this, this.lang("Pm.Ti.PleaseSelectProduct"));
+                    return;
+                }
+
+                var count = TestItems.Where(d => d.IsSelected).Count();
+                if (count <= 0)
+                {
+                    ToastHelper.Warning(this, this.lang("HMI.NoDeleteData"));
+                    return;
+                }
+                if (MsgBoxHelper.WarnMessage(this, this.lang("HMI.MakeSureDeletes", count))
+                    == MessageBoxResult.OK)
+                {
+                    var temp = TestItems.Where(d => !d.IsSelected).ToList();
+                    TestItemManager.SaveTestItemByProduct(SelectedProduct.Name, temp);
+                    LoadTestItems();
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("Delete Batch Data Failed.", ex);
+                ToastHelper.Error(Window.GetWindow(this), $"{this.lang("App.DeleteFailed")}: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鍒犻櫎鍙傛暟
+        /// </summary>
+        /// <param name="data"></param>
+        private void DeleteData(TestItemData data)
+        {
+            try
+            {
+                if (SelectedProduct == null)
+                {
+                    ToastHelper.Warning(this, this.lang("Pm.Ti.PleaseSelectProduct"));
+                    return;
+                }
+
+                if (MsgBoxHelper.WarnMessage(this, this.lang("HMI.MakeSureDeleteSingle", data.Name))
+                    == MessageBoxResult.OK)
+                {
+                    int index = TestItems.IndexOf(data);
+                    if (index < 0)
+                    {
+                        ToastHelper.Error(this, this.lang("App.ParameterException"));
+                        return;
+                    }
+                    var temp = XmlHelper.DeepClone(TestItems);
+                    temp.RemoveAt(index);
+                    TestItemManager.SaveTestItemByProduct(SelectedProduct.Name, temp);
+                    LoadTestItems();
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("Delete Test Item Data Failed.", ex);
+                ToastHelper.Error(this, $"{this.lang("App.DeleteFailed")}: {ex.Message}");
+                return;
+            }
+        }
+
+        /// <summary>
+        /// 瀵煎嚭鏁版嵁
+        /// </summary>
+        private void ExportData()
+        {
+            try
+            {
+                if (SelectedProduct == null)
+                {
+                    ToastHelper.Warning(this, this.lang("Pm.Ti.PleaseSelectProduct"));
+                    return;
+                }
+
+                SaveFileDialog sfd = new SaveFileDialog();
+
+                sfd.Filter = "";
+                sfd.DefaultExt = "xls";
+                sfd.FileName = $"{SelectedProduct.Name}{this.lang("Pm.Ti.Title")}";
+                if (sfd.ShowDialog() == true)
+                {
+                    string targetPath = sfd.FileName;
+                    ExcelHelper.ExportExcel(targetPath, TestItems);
+                    ToastHelper.Success(Window.GetWindow(this), this.lang("App.ExportSuccess"));
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("Export Test Item Failed.", ex);
+                MsgBoxHelper.Error(Window.GetWindow(this), $"{this.lang("App.ExportFailed")}: {ex.Message}");
+            }
+        }
+
+        private void ImportData()
+        {
+            try
+            {
+                if (SelectedProduct == null)
+                {
+                    ToastHelper.Warning(this, this.lang("Pm.Ti.PleaseSelectProduct"));
+                    return;
+                }
+
+                OpenFileDialog openFileDialog = new OpenFileDialog();
+                openFileDialog.Filter = "execl(*.xlsx;*.xls)|*.xlsx;*.xls";
+                openFileDialog.Title = this.lang("App.SelectFile");
+                openFileDialog.Multiselect = false;
+                if (openFileDialog.ShowDialog() == true)
+                {
+                    List<TestItemData> datas = ExcelHelper.ReadFromExcel<TestItemData>(openFileDialog.FileName);
+                    TestItemManager.SaveTestItemByProduct(SelectedProduct.Name, TestItems);
+                    LoadTestItems();
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("Export Test Item Failed.", ex);
+                MsgBoxHelper.Error(Window.GetWindow(this), $"{this.lang("App.ExportFailed")}: {ex.Message}");
+            }
+        }
+
+        private void LoadProducts()
+        {
+            try
+            {
+                ObservableCollection<ProductData> products =
+                ParameterManager.GetProducts<ObservableCollection<ProductData>>()
+                ?? new ObservableCollection<ProductData>();
+                Products = products;
+            }
+            catch (Exception ex)
+            {
+                Products = new ObservableCollection<ProductData>();
+                log.Error("Load Products Failed.", ex);
+                ToastHelper.Error(this, this.lang("App.RefreshFailed"));
+            }
+        }
+
+        private void PluginControl_Loaded(object sender, RoutedEventArgs e)
+        {
+            LoadProducts();
+            Loaded -= PluginControl_Loaded;
+        }
+    }
+}
diff --git a/HMI.ParameterManage/TestItemModal.xaml b/HMI.ParameterManage/TestItemModal.xaml
new file mode 100644
index 0000000..87ce82f
--- /dev/null
+++ b/HMI.ParameterManage/TestItemModal.xaml
@@ -0,0 +1,71 @@
+锘�<pu:WindowX x:Class="HMI.ParameterManage.TestItemModal"
+            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:local="clr-namespace:HMI.ParameterManage"
+            xmlns:pw="https://opensource.panuon.com/wpf"
+            xmlns:pu="https://opensource.panuon.com/wpf-ui"
+            xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
+            xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
+            mc:Ignorable="d"
+            Title="Form"
+            SizeToContent="Height"
+            Width="600"
+            WindowStartupLocation="CenterOwner"
+            BorderBrush="#3E3E45"
+            BorderThickness="1"
+            Foreground="Black"
+            Style="{StaticResource ModalWindow}">
+    <Grid Margin="10">
+        <StackPanel x:Name="formPanel">
+            <pu:FormGroup x:Name="FgName" Style="{StaticResource InputFormGroupStyle}" 
+                          Header="{DynamicResource Pm.Ti.Name}" GroupName="MyFormGroup"
+                          dp:FormGroupHelper.IsRequired="True"
+                          dp:FormGroupHelper.ValidateMethod="ValidateName">
+                <TextBox x:Name="TbName" Style="{StaticResource EditorTextBoxStyle}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgType" Style="{StaticResource InputFormGroupStyle}" 
+                          Header="{DynamicResource Pm.Ti.Type}"
+                          dp:FormGroupHelper.IsRequired="True" GroupName="MyFormGroup">
+                <ComboBox x:Name="CbType" Style="{StaticResource FormComboxStyle}" SelectedValuePath="Content">
+                    <ComboBoxItem>PassFail</ComboBoxItem>
+                    <ComboBoxItem>Int</ComboBoxItem>
+                    <ComboBoxItem>Double</ComboBoxItem>
+                    <ComboBoxItem>String</ComboBoxItem>
+                </ComboBox>
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgEnable" Style="{StaticResource InputFormGroupStyle}" 
+                  Header="{DynamicResource Pm.Ti.Shineng}" GroupName="MyFormGroup">
+                <CheckBox x:Name="CoEnable" />
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgLimitDown" Style="{StaticResource InputFormGroupStyle}" 
+      Header="{DynamicResource Pm.Ti.LimitDown}" GroupName="MyFormGroup">
+                <pu:NumberInput x:Name="NiLimitDown" Style="{StaticResource FormNumberInput}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgLimitUp" Style="{StaticResource InputFormGroupStyle}" 
+Header="{DynamicResource Pm.Ti.LimitUp}" GroupName="MyFormGroup">
+                <pu:NumberInput x:Name="NiLimitUp" Style="{StaticResource FormNumberInput}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgStrValue" Style="{StaticResource InputFormGroupStyle}" 
+Header="{DynamicResource Pm.Ti.StrValue}" GroupName="MyFormGroup">
+                <TextBox x:Name="TbStrValue" Style="{StaticResource EditorTextBoxStyle}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgOffset" Style="{StaticResource InputFormGroupStyle}" 
+Header="{DynamicResource Pm.Ti.Offset}" GroupName="MyFormGroup">
+                <pu:NumberInput x:Name="NiOffset" Style="{StaticResource FormNumberInput}" />
+            </pu:FormGroup>
+
+            <pu:FormGroup x:Name="FgSource" Style="{StaticResource InputFormGroupStyle}" 
+Header="{DynamicResource Pm.Ti.Source}" GroupName="MyFormGroup">
+                <ComboBox x:Name="CbSource" SelectedValuePath="Content" Style="{StaticResource FormComboxStyle}" />
+            </pu:FormGroup>
+        </StackPanel>
+    </Grid>
+</pu:WindowX>
diff --git a/HMI.ParameterManage/TestItemModal.xaml.cs b/HMI.ParameterManage/TestItemModal.xaml.cs
new file mode 100644
index 0000000..735a000
--- /dev/null
+++ b/HMI.ParameterManage/TestItemModal.xaml.cs
@@ -0,0 +1,114 @@
+锘縰sing HMI.Core.Util;
+using HMI.ParameterManage.Entity;
+using HMI.UI.DpUtil;
+using HMI.UI.Util;
+using Panuon.WPF.UI;
+using System;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+
+namespace HMI.ParameterManage
+{
+    /// <summary>
+    /// ModalWindow.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class TestItemModal : WindowX
+    {
+        public TestItemModal()
+        {
+            this.DataContext = this;
+            InitializeComponent();
+        }
+
+        public TestItemData Data { get; set; }
+        public ObservableCollection<TestItemData> Datasource { get; set; }
+
+        private bool _isProcessing;
+
+        protected override void OnClosing(CancelEventArgs e)
+        {
+            //Press Continue Button
+            if (DialogResult == true)
+            {
+                if (_isProcessing)
+                {
+                    e.Cancel = true;
+                    return;
+                }
+                var okButton = WindowXModalDialog.GetOKButton(this);
+                try
+                {
+                    _isProcessing = true;
+                    ButtonHelper.SetIsPending(okButton, true);
+                    if (!ValidateForm())
+                    {
+                        e.Cancel = true;
+                        return;
+                    }
+
+                    SaveOrUpdateParameter(Data);
+                }
+                catch (Exception ex)
+                {
+                    ToastHelper.Error(this, $"Error: {ex.Message}");
+                    e.Cancel = true;
+                    return;
+                }
+                finally
+                {
+                    _isProcessing = false;
+                    ButtonHelper.SetIsPending(okButton, false);
+                }
+            }
+            base.OnClosing(e);
+        }
+
+        public bool ValidateName(object value, out string err)
+        {
+            err = null;
+            if (Datasource?.FirstOrDefault(e => e.Name == value.ToString()) != null)
+            {
+                err = this.lang("App.MustBeUnique", this.lang("Pm.Parameter.Name"));
+                return false;
+            }
+            return true;
+        }
+
+        private void SaveOrUpdateParameter(TestItemData data)
+        {
+        }
+
+        private bool ValidateForm()
+        {
+            bool flag = FormGroupHelper.ValidateForm(formPanel);
+            if (flag)
+            {
+                Data.Name = TbName.Text;
+                Data.Type = CbType.Text;
+                Data.Enable = CoEnable.IsChecked == true;
+                Data.LimitDown = NiLimitDown.Value ?? 0;
+                Data.LimitUp = NiLimitUp.Value ?? 0;
+                Data.StrValue = TbStrValue.Text;
+                Data.Offset = NiOffset.Value ?? 0;
+                Data.Source = CbSource.Text;
+            }
+            return flag;
+        }
+
+        public bool? Open(TestItemData data, ObservableCollection<TestItemData> datasource)
+        {
+            Datasource = datasource;
+            Data = data;
+            TbName.Text = Data.Name;
+            CbType.Text = Data.Type;
+            CoEnable.IsChecked = Data.Enable;
+            NiLimitDown.Value = Data.LimitDown;
+            NiLimitUp.Value = Data.LimitUp;
+            TbStrValue.Text = Data.StrValue;
+            NiOffset.Value = Data.Offset;
+            CbSource.Text = Data.Source;
+            return this.ShowDialog();
+        }
+    }
+}
diff --git a/HMI.ParameterManage/Util/ParameterManager.cs b/HMI.ParameterManage/Util/ParameterManager.cs
index 8374f76..32e3115 100644
--- a/HMI.ParameterManage/Util/ParameterManager.cs
+++ b/HMI.ParameterManage/Util/ParameterManager.cs
@@ -1,20 +1,34 @@
 锘縰sing HMI.Core.Util;
 using HMI.ParameterManage.Entity;
+using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.IO;
 
 namespace HMI.ParameterManage.Util
 {
+    /// <summary>
+    /// TODO 浼樺寲鏁版嵁婧愶紝闇�瑕佹敮鎸乻qlite鍜寈ml
+    /// </summary>
     public class ParameterManager
     {
-        public static List<ProductData> GetProducts()
+        /// <summary>
+        /// 鑾峰彇淇濆瓨鐨勪骇鍝�
+        /// </summary>
+        /// <returns></returns>
+        public static T GetProducts<T>() where T : IList<ProductData>
         {
             string path = Path.Combine(ParameterManage.FileFolder, "Product.setting");
-            if (!File.Exists(path)) return new List<ProductData>();
-            List<ProductData> datas = XmlHelper.DeserializeFromXml<List<ProductData>>(path);
+            if (!File.Exists(path)) return default(T);
+            T datas = XmlHelper.DeserializeFromXml<T>(path);
             return datas;
         }
 
+        /// <summary>
+        /// 鏍规嵁浜у搧鍚嶇О鑾峰彇浜у搧鍙傛暟
+        /// </summary>
+        /// <param name="productName"></param>
+        /// <returns></returns>
         public static List<ParameterData> GetParametersByProductName(string productName)
         {
             string path = Path.Combine(ParameterManage.FileFolder, "Parameter", $"Parameter_{productName}.setting");
@@ -22,5 +36,34 @@
             List<ParameterData> datas = XmlHelper.DeserializeFromXml<List<ParameterData>>(path);
             return datas;
         }
+
+        /// <summary>
+        /// 鑾峰彇閫氱敤鍙傛暟
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <returns></returns>
+        public static T GetCommonParameter<T>() where T : IList<ParameterData>
+        {
+            string path = Path.Combine(ParameterManage.FileFolder, "CommonParameter.setting");
+            if (!File.Exists(path)) return default(T);
+            T datas = XmlHelper.DeserializeFromXml<T>(path);
+            return datas;
+        }
+
+        /// <summary>
+        /// 淇濆瓨閫氱敤鍙傛暟
+        /// </summary>
+        /// <param name="datas"></param>
+        /// <exception cref="Exception"></exception>
+        public static void SaveCommonParameter(IList<ParameterData> datas)
+        {
+            if (datas == null)
+            {
+                throw new Exception("涓嶈兘涓簄ull");
+            }
+            string path = Path.Combine(ParameterManage.FileFolder, "CommonParameter.setting");
+            XmlHelper.SerializeToXml(path, datas);
+        }
+
     }
 }
diff --git a/HMI.ParameterManage/Util/TestItemManager.cs b/HMI.ParameterManage/Util/TestItemManager.cs
new file mode 100644
index 0000000..4478c04
--- /dev/null
+++ b/HMI.ParameterManage/Util/TestItemManager.cs
@@ -0,0 +1,44 @@
+锘縰sing HMI.Core.Util;
+using HMI.ParameterManage.Entity;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+
+namespace HMI.ParameterManage.Util
+{
+    /// <summary>
+    /// TODO 浼樺寲鏁版嵁婧愶紝闇�瑕佹敮鎸乻qlite鍜寈ml
+    /// </summary>
+    public class TestItemManager
+    {
+        /// <summary>
+        /// 鏍规嵁浜у搧鑾峰彇娴嬭瘯椤�
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <returns></returns>
+        public static T GetTestItemByProduct<T>(string product) where T : IList<TestItemData>
+        {
+            string path = Path.Combine(TestItem.FileFolder, $"TestItem_{product}.setting");
+            if (!File.Exists(path)) return default(T);
+            T datas = XmlHelper.DeserializeFromXml<T>(path);
+            return datas;
+        }
+
+        /// <summary>
+        /// 鏍规嵁浜у搧淇濆瓨娴嬭瘯椤�
+        /// </summary>
+        /// <param name="datas"></param>
+        /// <exception cref="Exception"></exception>
+        public static void SaveTestItemByProduct(string product, IList<TestItemData> datas)
+        {
+            if (datas == null)
+            {
+                throw new Exception("涓嶈兘涓簄ull");
+            }
+            string path = Path.Combine(TestItem.FileFolder, $"TestItem_{product}.setting");
+            XmlHelper.SerializeToXml(path, datas);
+        }
+
+    }
+}
diff --git a/HMI.UI/Style/FormStyle.xaml b/HMI.UI/Style/FormStyle.xaml
index 8664b17..1cd04c2 100644
--- a/HMI.UI/Style/FormStyle.xaml
+++ b/HMI.UI/Style/FormStyle.xaml
@@ -132,6 +132,7 @@
         <Setter Property="pu:ComboBoxHelper.ItemsSelectedForeground" Value="Black"/>
         <Setter Property="pu:ComboBoxHelper.HoverBorderBrush" Value="#007ACC" />
         <Setter Property="pu:ComboBoxHelper.FocusedBorderBrush" Value="#007ACC" />
+        <Setter Property="pu:ComboBoxHelper.ClearButtonVisibility" Value="VisibleOnNonnull" />
         <Setter Property="Height" Value="35" />
         <Setter Property="Margin" Value="0" />
         <Setter Property="Background" Value="#FFF" />
diff --git a/HMI.UI/Style/PanuonResource.xaml b/HMI.UI/Style/PanuonResource.xaml
index 42c9a94..01337d5 100644
--- a/HMI.UI/Style/PanuonResource.xaml
+++ b/HMI.UI/Style/PanuonResource.xaml
@@ -175,7 +175,6 @@
 
     <Style x:Key="DefaultDataGrid" TargetType="DataGrid" BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridStyle}}">
         <Style.Resources>
-
             <Style TargetType="DataGridCell" 
                    BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridCellStyle}}">
                 <Setter Property="Background" Value="Transparent"/>
@@ -188,13 +187,11 @@
                 <Setter Property="VerticalAlignment" Value="Center"/>
                 <Setter Property="Padding" Value="5"/>
             </Style>-->
-
             <Style TargetType="DataGridColumnHeader" 
        BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridColumnHeaderStyle}}">
                 <Setter Property="Padding" Value="10"/>
                 <Setter Property="BorderThickness" Value="0"/>
             </Style>
-
         </Style.Resources>
         <Setter Property="pu:DataGridHelper.ColumnHeaderPadding" Value="0"/>
         <Setter Property="RowHeight" Value="30"/>
@@ -231,7 +228,6 @@
         <Setter Property="pu:DataGridHelper.RowSelectedBackground" Value="{StaticResource PrimaryColor}"/>
         <Setter Property="pu:DataGridHelper.RowSelectedForeground" Value="White"/>
         <Setter Property="pu:DataGridHelper.RowHeaderBorderThickness" Value="10,0,0,0"/>
-        <Setter Property="pu:DataGridHelper.RowHeaderBackground" Value="Red"/>
         <Setter Property="pu:DataGridHelper.ColumnHeaderBorderThickness" Value="0"/>
         <Setter Property="pu:DataGridHelper.ColumnHeaderPadding" Value="0"/>
     </Style>
diff --git a/HMI.UI/Util/MsgBoxHelper.cs b/HMI.UI/Util/MsgBoxHelper.cs
index 8a20c3e..8c01e3f 100644
--- a/HMI.UI/Util/MsgBoxHelper.cs
+++ b/HMI.UI/Util/MsgBoxHelper.cs
@@ -8,7 +8,7 @@
     public class MsgBoxHelper
     {
         #region Warn
-        public static MessageBoxResult WarnMessage(Window owner,
+        public static MessageBoxResult WarnMessage(DependencyObject owner,
             string message,
             string caption = null,
             MessageBoxButton button = MessageBoxButton.OKCancel,
@@ -48,7 +48,7 @@
         #endregion
 
         #region Info
-        public static MessageBoxResult InfoMessage(Window owner,
+        public static MessageBoxResult InfoMessage(DependencyObject owner,
             string message,
             string caption = null,
             MessageBoxButton button = MessageBoxButton.OKCancel,
diff --git a/HMI.UI/Util/ToastHelper.cs b/HMI.UI/Util/ToastHelper.cs
index 9f4421f..2156bdc 100644
--- a/HMI.UI/Util/ToastHelper.cs
+++ b/HMI.UI/Util/ToastHelper.cs
@@ -40,39 +40,55 @@
             if (Instance == null) { Instance = helper; }
         }
 
-        public static void Success(Window window,
+        public static void Success(DependencyObject dp,
             string message,
             MessageBoxIcon icon = MessageBoxIcon.Success,
             ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
             ToastWindow targetWindow = ToastWindow.ActiveWindow)
         {
+            if (!(dp is Window window))
+            {
+                window = Window.GetWindow(dp);
+            }
             Instance.Success(window, message, icon, position, offset, durationMs, targetWindow);
         }
 
-        public static void Warning(Window window,
+        public static void Warning(DependencyObject dp,
             string message,
             MessageBoxIcon icon = MessageBoxIcon.Warning,
             ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
             ToastWindow targetWindow = ToastWindow.ActiveWindow)
         {
+            if (!(dp is Window window))
+            {
+                window = Window.GetWindow(dp);
+            }
             Instance.Warning(window, message, icon, position, offset, durationMs, targetWindow);
         }
 
-        public static void Error(Window window,
+        public static void Error(DependencyObject dp,
             string message,
             MessageBoxIcon icon = MessageBoxIcon.Error,
             ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
             ToastWindow targetWindow = ToastWindow.ActiveWindow)
         {
+            if (!(dp is Window window))
+            {
+                window = Window.GetWindow(dp);
+            }
             Instance.Error(window, message, icon, position, offset, durationMs, targetWindow);
         }
 
-        public static void Info(Window window,
+        public static void Info(DependencyObject dp,
             string message,
             MessageBoxIcon icon = MessageBoxIcon.Info,
             ToastPosition position = ToastPosition.Top, double offset = 0, int durationMs = 3000,
             ToastWindow targetWindow = ToastWindow.ActiveWindow)
         {
+            if (!(dp is Window window))
+            {
+                window = Window.GetWindow(dp);
+            }
             Instance.Info(window, message, icon, position, offset, durationMs, targetWindow);
         }
     }
diff --git a/HMI.Util/ExcelColumn.cs b/HMI.Util/ExcelColumn.cs
new file mode 100644
index 0000000..500b8e9
--- /dev/null
+++ b/HMI.Util/ExcelColumn.cs
@@ -0,0 +1,19 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HMI.Util
+{
+    [AttributeUsage(AttributeTargets.Property)]
+    public class ExcelColumnAttribute : Attribute
+    {
+        public string Name { get; private set; }
+
+        public ExcelColumnAttribute(string name)
+        {
+            this.Name = name;
+        }
+    }
+}
diff --git a/HMI.Util/ExcelHelper.cs b/HMI.Util/ExcelHelper.cs
new file mode 100644
index 0000000..7a6f416
--- /dev/null
+++ b/HMI.Util/ExcelHelper.cs
@@ -0,0 +1,137 @@
+锘縰sing NPOI.HSSF.UserModel;
+using NPOI.SS.UserModel;
+using NPOI.XSSF.UserModel;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+namespace HMI.Util
+{
+    public class ExcelHelper
+    {
+        public static void ExportExcel<T>(string targetPath, IList<T> datas)
+        {
+            Type type = typeof(T);
+            var properties = PropertyHelper.GetAtterProperty<T, ExcelColumnAttribute>(out var attrs);
+            if (properties == null || properties.Count <= 0)
+            {
+                throw new Exception("No Attribute");
+            }
+            IWorkbook workbook = null;
+            if (targetPath.EndsWith(".xlsx"))
+            {
+                workbook = new XSSFWorkbook();
+            }
+            else if (targetPath.EndsWith(".xls"))
+            {
+                workbook = new HSSFWorkbook();
+            }
+            workbook.CreateSheet("sheet1");////鏂板缓涓�涓伐浣滆〃
+            ISheet SheetOne = workbook.GetSheet("Sheet1");
+            IRow row1 = SheetOne.CreateRow(0);
+            for (int i = 0; i < properties.Count; i++)
+            {
+                row1.CreateCell(i).SetCellValue((attrs[i]).Name);
+            }
+
+            for (int i = 0; i < datas.Count(); i++)
+            {
+                IRow rowTemp = SheetOne.CreateRow(i + 1);
+                for (int j = 0; j < properties.Count; j++)
+                {
+                    var value = properties[j].GetValue(datas[i]);
+                    rowTemp.CreateCell(j).SetCellValue(value.ToString());
+                }
+            }
+
+            using (FileStream fs = new FileStream(targetPath, FileMode.OpenOrCreate))
+            {
+                workbook.Write(fs);
+            }
+        }
+
+
+        public static List<T> ReadFromExcel<T>(string sourcePath)
+        {
+            Type type = typeof(T);
+            var properties = PropertyHelper.GetAtterProperty<T, ExcelColumnAttribute>(out var attrs);
+            if (properties == null || properties.Count <= 0)
+            {
+                throw new Exception("No Attribute");
+            }
+            Dictionary<string, PropertyInfo> dict = new Dictionary<string, PropertyInfo>();
+            for (int i = 0; i < attrs.Count; i++)
+            {
+                dict.Add(attrs[i].Name, properties[i]);
+            }
+            using (FileStream fileStream1 = new FileStream(sourcePath, FileMode.Open, FileAccess.Read))
+            {
+                IWorkbook workbook = null;
+                if (sourcePath.EndsWith(".xlsx"))
+                {
+                    workbook = new XSSFWorkbook(fileStream1);
+                }
+                else if (sourcePath.EndsWith(".xls"))
+                {
+                    workbook = new HSSFWorkbook(fileStream1);
+                }
+
+                ISheet sheet = workbook.GetSheetAt(0);
+                IRow titleRow = sheet.GetRow(0);
+
+                List<T> result = new List<T>();
+                IRow row;
+                for (int i = 1; i < (sheet.LastRowNum + 1); i++)
+                {
+                    row = sheet.GetRow(i);
+                    if (row != null)
+                    {
+                        T obj = (T)Activator.CreateInstance(type);
+                        bool isEmpty = true;
+                        for (int j = 0; j < row.Count(); j++)
+                        {
+                            string title = titleRow.GetCell(j).StringCellValue;
+                            if (dict.ContainsKey(title))
+                            {
+                                CellType ct = row.GetCell(j).CellType;
+                                object value = null;
+                                if (ct == CellType.String)
+                                    value = row.GetCell(j).StringCellValue;
+                                else if (ct == CellType.Numeric)
+                                    value = row.GetCell(j).NumericCellValue;
+                                else if (ct == CellType.Boolean)
+                                    value = row.GetCell(j).BooleanCellValue;
+
+                                var prop = dict[title];
+                                if (prop.PropertyType is string)
+                                {
+                                    value = Convert.ToString(value);
+                                }
+                                else if (prop.PropertyType is double || prop.PropertyType is double?)
+                                {
+                                    value = Convert.ToDouble(value);
+                                }
+                                else if (prop.PropertyType is int || prop.PropertyType is int?)
+                                {
+                                    value = Convert.ToInt16(value);
+                                }
+                                else if (prop.PropertyType is bool || prop.PropertyType is bool?)
+                                {
+                                    value = Convert.ToBoolean(value);
+                                }
+
+                                prop.SetValue(obj, value);
+                                isEmpty = false;
+                            }
+                        }
+                        if (!isEmpty) result.Add(obj);
+                    }
+                }
+                workbook.Close();
+                return result;
+            }
+        }
+    }
+}
diff --git a/HMI.Util/HMI.Util.csproj b/HMI.Util/HMI.Util.csproj
new file mode 100644
index 0000000..b50268a
--- /dev/null
+++ b/HMI.Util/HMI.Util.csproj
@@ -0,0 +1,69 @@
+<?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>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{AFEA100A-9223-448D-AB10-3015D93DD98C}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>HMI.Util</RootNamespace>
+    <AssemblyName>HMI.Util</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="ICSharpCode.SharpZipLib, Version=1.0.0.999, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
+      <HintPath>..\WPFProject\packages\SharpZipLib.1.0.0\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
+    </Reference>
+    <Reference Include="NPOI, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
+      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.dll</HintPath>
+    </Reference>
+    <Reference Include="NPOI.OOXML, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
+      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.OOXML.dll</HintPath>
+    </Reference>
+    <Reference Include="NPOI.OpenXml4Net, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
+      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.OpenXml4Net.dll</HintPath>
+    </Reference>
+    <Reference Include="NPOI.OpenXmlFormats, Version=2.4.1.0, Culture=neutral, PublicKeyToken=0df73ec7942b34e1, processorArchitecture=MSIL">
+      <HintPath>..\WPFProject\packages\NPOI.2.4.1\lib\net45\NPOI.OpenXmlFormats.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ExcelHelper.cs" />
+    <Compile Include="ExcelColumn.cs" />
+    <Compile Include="IniFileHelper.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="PropertyHelper.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>
\ No newline at end of file
diff --git a/HMI.Util/IniFileHelper.cs b/HMI.Util/IniFileHelper.cs
new file mode 100644
index 0000000..f6e7d4e
--- /dev/null
+++ b/HMI.Util/IniFileHelper.cs
@@ -0,0 +1,112 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace HMI.Util
+{
+    public class IniFileHelper
+    {
+        private static String filePath = "./default.ini";
+        static List<string> sections = new List<string>();
+        static List<string> keys = new List<string>();
+        #region API鍑芥暟澹版槑
+
+        [DllImport("kernel32")]
+        private static extern long WritePrivateProfileString(string section, string key,
+            string val, string filePath);
+
+        //闇�瑕佽皟鐢℅etPrivateProfileString鐨勯噸杞�
+        [DllImport("kernel32", EntryPoint = "GetPrivateProfileString")]
+        private static extern long GetPrivateProfileString(string section, string key,
+            string def, StringBuilder retVal, int size, string filePath);
+
+        [DllImport("kernel32", EntryPoint = "GetPrivateProfileString")]
+        private static extern uint GetPrivateProfileStringA(string section, string key,
+            string def, Byte[] retVal, int size, string filePath);
+
+        #endregion
+        public static List<string> ReadSections()
+        {
+            return ReadSections(filePath);
+        }
+
+        public static List<string> ReadSections(string iniFilename)
+        {
+            List<string> result = new List<string>();
+            Byte[] buf = new Byte[65536];
+            uint len = GetPrivateProfileStringA(null, null, null, buf, buf.Length, iniFilename);
+            int j = 0;
+            for (int i = 0; i < len; i++)
+                if (buf[i] == 0)
+                {
+                    result.Add(Encoding.Default.GetString(buf, j, i - j));
+                    j = i + 1;
+                }
+            return result;
+        }
+
+        public static List<string> ReadKeys(String SectionName)
+        {
+            return ReadKeys(SectionName, filePath);
+        }
+
+        public static List<string> ReadKeys(string SectionName, string iniFilename)
+        {
+            List<string> result = new List<string>();
+            Byte[] buf = new Byte[65536];
+            uint len = GetPrivateProfileStringA(SectionName, null, null, buf, buf.Length, iniFilename);
+            int j = 0;
+            for (int i = 0; i < len; i++)
+                if (buf[i] == 0)
+                {
+                    result.Add(Encoding.Default.GetString(buf, j, i - j));
+                    j = i + 1;
+                }
+            return result;
+        }
+        public static void SetFilePath(String filepath)
+        {
+            filePath = filepath;
+        }
+        #region 璇籌ni鏂囦欢
+
+        public static string ReadIniData(string Section, string Key, string NoText)
+        {
+            return ReadIniData(Section, Key, NoText, filePath);
+        }
+
+        public static string ReadIniData(string Section, string Key, string NoText, string iniFilePath)
+        {
+            if (File.Exists(iniFilePath))
+            {
+                StringBuilder temp = new StringBuilder(1024);
+                GetPrivateProfileString(Section, Key, NoText, temp, 1024, iniFilePath);
+                return temp.ToString();
+            }
+            else
+                return String.Empty;
+        }
+
+        #endregion
+
+        #region 鍐橧ni鏂囦欢
+
+
+        public static bool WriteIniData(string Section, string Key, string Value, string iniFilePath)
+        {
+            if (File.Exists(iniFilePath) == false)
+
+            {
+                FileStream FS = new FileStream(iniFilePath, FileMode.CreateNew);
+            }
+            long OpStation = WritePrivateProfileString(Section, Key, Value, iniFilePath);
+            if (OpStation == 0)
+                return false;
+            else
+                return true;
+        }
+        #endregion
+    }
+}
diff --git a/HMI.Util/Properties/AssemblyInfo.cs b/HMI.Util/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..79e2b2d
--- /dev/null
+++ b/HMI.Util/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+锘縰sing System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 鏈夊叧绋嬪簭闆嗙殑涓�鑸俊鎭敱浠ヤ笅
+// 鎺у埗銆傛洿鏀硅繖浜涚壒鎬у�煎彲淇敼
+// 涓庣▼搴忛泦鍏宠仈鐨勪俊鎭��
+[assembly: AssemblyTitle("HMI.Util")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("HMI.Util")]
+[assembly: AssemblyCopyright("Copyright 漏  2025")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 灏� ComVisible 璁剧疆涓� false 浼氫娇姝ょ▼搴忛泦涓殑绫诲瀷
+//瀵� COM 缁勪欢涓嶅彲瑙併�傚鏋滈渶瑕佷粠 COM 璁块棶姝ょ▼搴忛泦涓殑绫诲瀷
+//璇峰皢姝ょ被鍨嬬殑 ComVisible 鐗规�ц缃负 true銆�
+[assembly: ComVisible(false)]
+
+// 濡傛灉姝ら」鐩悜 COM 鍏紑锛屽垯涓嬪垪 GUID 鐢ㄤ簬绫诲瀷搴撶殑 ID
+[assembly: Guid("afea100a-9223-448d-ab10-3015d93dd98c")]
+
+// 绋嬪簭闆嗙殑鐗堟湰淇℃伅鐢变笅鍒楀洓涓�肩粍鎴�: 
+//
+//      涓荤増鏈�
+//      娆$増鏈�
+//      鐢熸垚鍙�
+//      淇鍙�
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/HMI.Util/PropertyHelper.cs b/HMI.Util/PropertyHelper.cs
new file mode 100644
index 0000000..2f1601c
--- /dev/null
+++ b/HMI.Util/PropertyHelper.cs
@@ -0,0 +1,30 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace HMI.Util
+{
+    class PropertyHelper
+    {
+        public static List<PropertyInfo> GetAtterProperty<T, A>(out List<A> attrs) where A : Attribute
+        {
+            var result = new List<PropertyInfo>();
+            attrs = new List<A>();
+
+            var type = typeof(T);
+            var attrType = typeof(A);
+
+            PropertyInfo[] properties = type.GetProperties();
+
+            foreach (var prop in properties)
+            {
+                if (Attribute.IsDefined(prop, attrType))
+                {
+                    result.Add(prop);
+                    attrs.Add((A)Attribute.GetCustomAttribute(prop, attrType));
+                }
+            }
+            return result;
+        }
+    }
+}
diff --git a/HMI.Util/packages.config b/HMI.Util/packages.config
new file mode 100644
index 0000000..5ed5cff
--- /dev/null
+++ b/HMI.Util/packages.config
@@ -0,0 +1,5 @@
+锘�<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="NPOI" version="2.4.1" targetFramework="net472" />
+  <package id="SharpZipLib" version="1.0.0" targetFramework="net472" />
+</packages>
\ No newline at end of file
diff --git a/Plugin.TestResult/ViewControl.xaml.cs b/Plugin.TestResult/ViewControl.xaml.cs
index ea740a9..be38c85 100644
--- a/Plugin.TestResult/ViewControl.xaml.cs
+++ b/Plugin.TestResult/ViewControl.xaml.cs
@@ -126,7 +126,7 @@
 
         public void LoadProduct()
         {
-            List<ProductData> products = ParameterManager.GetProducts();
+            List<ProductData> products = ParameterManager.GetProducts<List<ProductData>>();
             this.modelSelect.Items.Clear();
             foreach (var prd in products)
             {
diff --git a/Plugin.TsExecution/TsExecution.xaml.cs b/Plugin.TsExecution/TsExecution.xaml.cs
index 598abec..df139b3 100644
--- a/Plugin.TsExecution/TsExecution.xaml.cs
+++ b/Plugin.TsExecution/TsExecution.xaml.cs
@@ -193,12 +193,12 @@
 
         private void InitProducts()
         {
-            Products = new ObservableCollection<ProductData>(ParameterManager.GetProducts());
+            Products = ParameterManager.GetProducts<ObservableCollection<ProductData>>();
             if (Products != null && Products.Count > 0)
             {
                 List<ParameterData> parameters =
-                    ParameterManager.GetParametersByProductName(Products[0].Name);
-                string tmpNum = parameters.FirstOrDefault(p => p.Name == "SocketNum")?.Value?.ToString();
+                    ParameterManager.GetCommonParameter<List<ParameterData>>();
+                string tmpNum = parameters?.FirstOrDefault(p => p.Name == "SocketNum")?.Value?.ToString();
                 if (!string.IsNullOrEmpty(tmpNum))
                 {
                     this.socketNum = Convert.ToInt32(tmpNum);
diff --git a/WPFProject/3rds/3rds Libs.txt b/WPFProject/3rds/3rds Libs.txt
deleted file mode 100644
index 5f28270..0000000
--- a/WPFProject/3rds/3rds Libs.txt
+++ /dev/null
@@ -1 +0,0 @@
-锘�
\ No newline at end of file
diff --git a/WPFProject/Lang/zh-CN/ParamManage.json b/WPFProject/Lang/zh-CN/ParamManage.json
index dc8b04b..8f0519a 100644
--- a/WPFProject/Lang/zh-CN/ParamManage.json
+++ b/WPFProject/Lang/zh-CN/ParamManage.json
@@ -10,12 +10,25 @@
   "Pm.Parameter.Name": "鍚嶇О",
   "Pm.Parameter.Value": "鍊�",
   "Pm.Parameter.Unit": "鍗曚綅",
+  "Pm.Parameter.Type": "绫诲瀷",
   "Pm.Parameter.Desc": "鎻忚堪",
   "Pm.Parameter.Group": "鍒嗙粍",
+  "Pm.Parameter.Source": "鏉ユ簮",
   "Pm.Parameter.Add": "鏂板鍙傛暟",
   "Pm.Parameter.Edit": "缂栬緫鍙傛暟",
   "Pm.PleaseSelectProduct": "璇峰厛閫夋嫨涓�椤逛骇鍝�",
 
 
-  "Pm.Setting.Title":"鍙傛暟绠$悊"
+  "Pm.Setting.Title": "鍙傛暟绠$悊",
+
+  "Pm.Ti.Title": "娴嬭瘯椤圭鐞�",
+  "Pm.Ti.Name": "娴嬭瘯椤瑰悕绉�",
+  "Pm.Ti.Type": "娴嬭瘯绫诲瀷",
+  "Pm.Ti.Shineng": "浣胯兘",
+  "Pm.Ti.LimitDown": "涓嬮檺",
+  "Pm.Ti.LimitUp": "涓婇檺",
+  "Pm.Ti.StrValue": "瀛楃涓�",
+  "Pm.Ti.Offset": "琛ュ伩鍊�",
+  "Pm.Ti.Source": "鏉ユ簮",
+  "Pm.Ti.PleaseSelectProduct": "璇峰厛閫夋嫨浜у搧"
 }
\ No newline at end of file
diff --git a/WPFProject/Lang/zh-CN/app.json b/WPFProject/Lang/zh-CN/app.json
index 8b845e9..dcf2132 100644
--- a/WPFProject/Lang/zh-CN/app.json
+++ b/WPFProject/Lang/zh-CN/app.json
@@ -137,6 +137,6 @@
   "HMI.Role.Add": "鏂板瑙掕壊",
   "HMI.Role.Edit": "缂栬緫瑙掕壊",
 
-  "HMI.Line": "浜х嚎"锛�
-  "HMI.Station": "宸ョ珯"锛�
+  "HMI.Line": "浜х嚎",
+  "HMI.Station": "宸ョ珯"
 }
\ No newline at end of file
diff --git a/WPFProject/TextFile1.txt b/WPFProject/TextFile1.txt
index 7df8602..1a38b45 100644
--- a/WPFProject/TextFile1.txt
+++ b/WPFProject/TextFile1.txt
@@ -10,4 +10,23 @@
 move /y "$(TargeDir)HMI.ParameterManage.dll" $(TargeDir)Plugin\HMI.ParameterManage.dll"
 move /y "$(TargeDir)HMI.SettingControl.dll" $(TargeDir)Plugin\HMI.SettingControl.dll"
 move /y "$(TargeDir)Plugin.TestResult.dll" $(TargeDir)Plugin\Plugin.TestResult.dll"
-move /y "$(TargeDir)Plugin.TsExecution.dll" $(TargeDir)Plugin\Plugin.TsExecution.dll"
\ No newline at end of file
+move /y "$(TargeDir)Plugin.TsExecution.dll" $(TargeDir)Plugin\Plugin.TsExecution.dll"
+
+setlocal enabledelayedexpansion
+
+set "source_folder=$(TargetDir)"
+set "destination_folder=$(TargetDir)3rds"
+
+if not exist "%destination_folder%" mkdir "%destination_folder%"
+
+for /d %%D in ("%source_folder%\*") do (
+    if /I not "%%~nxD"=="Config" if /I not "%%~nxD"=="Icon" if /I not "%%~nxD"=="Lang" if /I not "%%~nxD"=="Libs" if /I not "%%~nxD"=="Log" if /I not "%%~nxD"=="Plugin" (
+        move "%%D" "%destination_folder%"
+    )
+)
+
+for %%F in ("%source_folder%\*") do (
+    if /I not "%%~nxF"=="$(TargetFileName)" if /I not "%%~nxF"=="$(TargetFileName).config" if /I not "%%~nxF"=="$(TargetName).pdb" if /I not "%%~nxF"=="clear.bat" (
+        move "%%F" "%destination_folder%"
+    )
+)
\ No newline at end of file
diff --git a/WPFProject/WPFProject.csproj b/WPFProject/WPFProject.csproj
index 8f53506..813964a 100644
--- a/WPFProject/WPFProject.csproj
+++ b/WPFProject/WPFProject.csproj
@@ -372,9 +372,6 @@
       <DependentUpon>Settings.settings</DependentUpon>
       <DesignTimeSharedInput>True</DesignTimeSharedInput>
     </Compile>
-    <Content Include="3rds\3rds Libs.txt">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-    </Content>
     <Content Include="Config\Put Your Config Here.txt">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
diff --git a/WPFProject/WPFProject.sln b/WPFProject/WPFProject.sln
index b4f1643..a6bc49f 100644
--- a/WPFProject/WPFProject.sln
+++ b/WPFProject/WPFProject.sln
@@ -37,6 +37,8 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.TsExecution", "..\Plugin.TsExecution\Plugin.TsExecution.csproj", "{202D9A7B-96B3-4AD4-9977-FC4263207A02}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMI.Util", "..\HMI.Util\HMI.Util.csproj", "{AFEA100A-9223-448D-AB10-3015D93DD98C}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -181,6 +183,14 @@
 		{202D9A7B-96B3-4AD4-9977-FC4263207A02}.Release|Any CPU.Build.0 = Release|Any CPU
 		{202D9A7B-96B3-4AD4-9977-FC4263207A02}.Release|x64.ActiveCfg = Release|Any CPU
 		{202D9A7B-96B3-4AD4-9977-FC4263207A02}.Release|x64.Build.0 = Release|Any CPU
+		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Debug|x64.Build.0 = Debug|Any CPU
+		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|Any CPU.Build.0 = Release|Any CPU
+		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|x64.ActiveCfg = Release|Any CPU
+		{AFEA100A-9223-448D-AB10-3015D93DD98C}.Release|x64.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

--
Gitblit v1.9.1