diff --git a/DMS.WPF.UnitTests/DMS.WPF.UnitTests.csproj b/DMS.WPF.UnitTests/DMS.WPF.UnitTests.csproj
new file mode 100644
index 0000000..f36b8b0
--- /dev/null
+++ b/DMS.WPF.UnitTests/DMS.WPF.UnitTests.csproj
@@ -0,0 +1,33 @@
+
+
+
+ net8.0-windows7.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DMS.WPF.UnitTests/UnitTest1.cs b/DMS.WPF.UnitTests/UnitTest1.cs
new file mode 100644
index 0000000..cbd3518
--- /dev/null
+++ b/DMS.WPF.UnitTests/UnitTest1.cs
@@ -0,0 +1,10 @@
+namespace DMS.WPF.UnitTests;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/DMS.WPF.UnitTests/ViewModelTest/DevicesViewModelTests.cs b/DMS.WPF.UnitTests/ViewModelTest/DevicesViewModelTests.cs
new file mode 100644
index 0000000..52bc15c
--- /dev/null
+++ b/DMS.WPF.UnitTests/ViewModelTest/DevicesViewModelTests.cs
@@ -0,0 +1,36 @@
+using AutoMapper;
+using DMS.Application.DTOs;
+using DMS.Application.Interfaces;
+using DMS.WPF.Services;
+using DMS.WPF.ViewModels;
+using DMS.WPF.ViewModels.Dialogs;
+using DMS.WPF.ViewModels.Items;
+using Microsoft.Extensions.DependencyInjection;
+using Moq;
+
+namespace DMS.WPF.UnitTests.ViewModelTest
+{
+ public class DevicesViewModelTests:BaseServiceTest
+ {
+ private readonly DevicesViewModel _devicesViewModel;
+
+
+ public DevicesViewModelTests()
+ {
+ _devicesViewModel= ServiceProvider.GetRequiredService();
+ }
+
+ [Fact]
+ public async Task AddDevice_Test()
+ {
+ // Arrange
+
+ // Act
+ await _devicesViewModel.AddDevice();
+
+
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/DMS.WPF/Profiles/MappingProfile.cs b/DMS.WPF/Profiles/MappingProfile.cs
new file mode 100644
index 0000000..f219148
--- /dev/null
+++ b/DMS.WPF/Profiles/MappingProfile.cs
@@ -0,0 +1,24 @@
+using AutoMapper;
+using DMS.Application.DTOs;
+using DMS.WPF.ViewModels.Items;
+
+namespace DMS.WPF.Profiles
+{
+ public class MappingProfile : Profile
+ {
+ public MappingProfile()
+ {
+ CreateMap().ConstructUsing(src => new DeviceItemViewModel(src));
+ CreateMap();
+ CreateMap()
+ .ForMember(dest => dest.Children, opt => opt.Ignore())
+ .ConstructUsing(src => new MenuBeanItemViewModel(src, null)); // 假设 NavigationService 可以通过依赖注入获取或在ViewModel中处理
+ CreateMap().ConstructUsing(src => new MqttServerItemViewModel(src));
+ CreateMap().ConstructUsing(src => new UserItemViewModel(src));
+ CreateMap().ConstructUsing(src => new VariableHistoryItemViewModel(src));
+ CreateMap().ConstructUsing(src => new VariableItemViewModel(src));
+ CreateMap().ConstructUsing(src => new VariableMqttAliasItemViewModel(src));
+ CreateMap().ConstructUsing(src => new VariableTableItemViewModel(src));
+ }
+ }
+}
\ No newline at end of file
diff --git a/DMS.WPF/ViewModels/Dialogs/DialogViewModelBase.cs b/DMS.WPF/ViewModels/Dialogs/DialogViewModelBase.cs
new file mode 100644
index 0000000..1eadab3
--- /dev/null
+++ b/DMS.WPF/ViewModels/Dialogs/DialogViewModelBase.cs
@@ -0,0 +1,27 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using System;
+using System.Threading.Tasks;
+
+namespace DMS.WPF.ViewModels.Dialogs
+{
+ public abstract partial class DialogViewModelBase : ObservableObject
+ {
+ [ObservableProperty]
+ private string _title;
+
+ [ObservableProperty]
+ private string _primaryButContent;
+
+ public event Func CloseRequested;
+
+ [RelayCommand]
+ protected virtual async Task Close(TResult result)
+ {
+ if (CloseRequested != null)
+ {
+ await CloseRequested(result);
+ }
+ }
+ }
+}
\ No newline at end of file