diff --git a/BaseFrame/App.xaml b/BaseFrame/App.xaml
new file mode 100644
index 0000000..363f5af
--- /dev/null
+++ b/BaseFrame/App.xaml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BaseFrame/App.xaml.cs b/BaseFrame/App.xaml.cs
new file mode 100644
index 0000000..f2aa19b
--- /dev/null
+++ b/BaseFrame/App.xaml.cs
@@ -0,0 +1,47 @@
+using BaseFrame.ViewModels;
+using BaseFrame.ViewModels.Dialogs;
+using BaseFrame.Views;
+using BaseFrame.Views.Dialogs;
+using Castle.DynamicProxy;
+using ECCS.Views;
+using System.Configuration;
+using System.Data;
+using System.Reflection;
+using System.Windows;
+using static System.Runtime.InteropServices.JavaScript.JSType;
+
+namespace BaseFrame
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : PrismApplication
+ {
+ protected override Window CreateShell()
+ {
+ return Container.Resolve();
+ }
+ protected override void OnInitialized()
+ {
+ base.OnInitialized();
+
+ var regionManager = Container.Resolve();
+ regionManager.RequestNavigate("ShellViewManager", "MonitorView");
+ }
+ protected override void RegisterTypes(IContainerRegistry containerRegistry)
+ {
+ //注册视图
+ containerRegistry.RegisterForNavigation("MonitorView");
+ containerRegistry.RegisterForNavigation("SettingView");
+ containerRegistry.RegisterForNavigation("UpdateInfoView");
+ //注册弹窗
+ containerRegistry.RegisterDialog("MessageBox");
+
+
+
+ }
+
+
+ }
+
+}
diff --git a/BaseFrame/AssemblyInfo.cs b/BaseFrame/AssemblyInfo.cs
new file mode 100644
index 0000000..b0ec827
--- /dev/null
+++ b/BaseFrame/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/BaseFrame/Converters/BooleanToVisibilityConverter.cs b/BaseFrame/Converters/BooleanToVisibilityConverter.cs
new file mode 100644
index 0000000..52b6c7d
--- /dev/null
+++ b/BaseFrame/Converters/BooleanToVisibilityConverter.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Data;
+
+namespace BaseFrame.Converters
+{
+ public class BooleanToVisibilityConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ bool input = value is bool b && b;
+ bool invert = parameter?.ToString()?.ToLower() == "invert";
+
+ if (invert)
+ input = !input;
+
+ return input ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ bool output = value is Visibility v && v == Visibility.Visible;
+ bool invert = parameter?.ToString()?.ToLower() == "invert";
+
+ if (invert)
+ output = !output;
+
+ return output;
+ }
+ }
+}
diff --git a/BaseFrame/ECCS.csproj b/BaseFrame/ECCS.csproj
new file mode 100644
index 0000000..9be13d0
--- /dev/null
+++ b/BaseFrame/ECCS.csproj
@@ -0,0 +1,64 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
diff --git a/BaseFrame/MultidimensionalTableConfig.cs b/BaseFrame/MultidimensionalTableConfig.cs
new file mode 100644
index 0000000..3a38b96
--- /dev/null
+++ b/BaseFrame/MultidimensionalTableConfig.cs
@@ -0,0 +1,66 @@
+using Newtonsoft.Json;
+using System;
+using System.IO;
+
+namespace ECCS
+{
+ public sealed class MultidimensionalTableConfig
+ {
+ private static readonly object _lock = new();
+ private static MultidimensionalTableConfig? _instance;
+
+ ///
+ /// 单例实例(首次访问自动从文件加载)
+ ///
+ public static MultidimensionalTableConfig Instance
+ {
+ get
+ {
+ lock (_lock)
+ {
+ if (_instance == null)
+ {
+ _instance = Load();
+ }
+ return _instance;
+ }
+ }
+ }
+
+ ///
+ /// exe 目录下的 config.json
+ ///
+ [JsonIgnore]
+ private static string ConfigFilePath =>
+ Path.Combine(AppContext.BaseDirectory, "config.json");
+
+ // ===== 配置项 =====
+ public string AppToken { get; set; }
+ public string DefaultTableId { get; set; }
+ public bool DefaultTableIsDeleted { get; set; }
+
+ ///
+ /// 保存到 config.json
+ ///
+ public void Save()
+ {
+ var json = JsonConvert.SerializeObject(this, Formatting.Indented);
+ File.WriteAllText(ConfigFilePath, json);
+ }
+
+ ///
+ /// 从 config.json 加载
+ ///
+ private static MultidimensionalTableConfig Load()
+ {
+ if (!File.Exists(ConfigFilePath))
+ {
+ return new MultidimensionalTableConfig();
+ }
+
+ var json = File.ReadAllText(ConfigFilePath);
+ return JsonConvert.DeserializeObject(json)
+ ?? new MultidimensionalTableConfig();
+ }
+ }
+}
diff --git a/BaseFrame/PubEvent/OverlayEvent.cs b/BaseFrame/PubEvent/OverlayEvent.cs
new file mode 100644
index 0000000..7060ba7
--- /dev/null
+++ b/BaseFrame/PubEvent/OverlayEvent.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BaseFrame.PubEvent
+{
+ public class OverlayEvent : PubSubEvent
+ {
+ }
+}
diff --git a/BaseFrame/PubEvent/WaitingEvent.cs b/BaseFrame/PubEvent/WaitingEvent.cs
new file mode 100644
index 0000000..d900f22
--- /dev/null
+++ b/BaseFrame/PubEvent/WaitingEvent.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BaseFrame.PubEvent
+{
+ public class WaitingEvent : PubSubEvent
+ {
+ }
+}
diff --git a/BaseFrame/Resources/Images/error.png b/BaseFrame/Resources/Images/error.png
new file mode 100644
index 0000000..de079d0
Binary files /dev/null and b/BaseFrame/Resources/Images/error.png differ
diff --git a/BaseFrame/Resources/Images/info.png b/BaseFrame/Resources/Images/info.png
new file mode 100644
index 0000000..8fed1fb
Binary files /dev/null and b/BaseFrame/Resources/Images/info.png differ
diff --git a/BaseFrame/Resources/Images/warning.png b/BaseFrame/Resources/Images/warning.png
new file mode 100644
index 0000000..1acd082
Binary files /dev/null and b/BaseFrame/Resources/Images/warning.png differ
diff --git a/BaseFrame/Resources/Styles/WindowStyle.xaml b/BaseFrame/Resources/Styles/WindowStyle.xaml
new file mode 100644
index 0000000..393226b
--- /dev/null
+++ b/BaseFrame/Resources/Styles/WindowStyle.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/BaseFrame/ViewModels/DialogViewModelBase.cs b/BaseFrame/ViewModels/DialogViewModelBase.cs
new file mode 100644
index 0000000..a04a98c
--- /dev/null
+++ b/BaseFrame/ViewModels/DialogViewModelBase.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ECCS.ViewModels
+{
+ public abstract class DialogViewModelBase : BindableBase,IDialogAware
+ {
+ public IEventAggregator _eventAggregator;
+ public DialogCloseListener RequestClose { get; set; }
+ public DialogViewModelBase(IContainerProvider containerProvider)
+ {
+ _eventAggregator = containerProvider.Resolve();
+ }
+ #region Dialog
+
+ public virtual bool CanCloseDialog() => true;
+ public virtual void OnDialogClosed() { }
+ public virtual void OnDialogOpened(IDialogParameters parameters) { }
+ #endregion
+ }
+}
diff --git a/BaseFrame/ViewModels/Dialogs/MessageBoxViewModel.cs b/BaseFrame/ViewModels/Dialogs/MessageBoxViewModel.cs
new file mode 100644
index 0000000..cb500c8
--- /dev/null
+++ b/BaseFrame/ViewModels/Dialogs/MessageBoxViewModel.cs
@@ -0,0 +1,123 @@
+using BaseFrame.PubEvent;
+using ControlzEx.Standard;
+using ECCS.ViewModels;
+using Prism.Commands;
+using Prism.Mvvm;
+using System.Windows.Input;
+
+namespace BaseFrame.ViewModels.Dialogs
+{
+ public class MessageBoxViewModel : DialogViewModelBase
+ {
+ #region 属性
+
+ private string _Title;
+ public string Title
+ {
+ get => _Title;
+ set => SetProperty(ref _Title, value);
+ }
+
+ private string _Message = "";
+ public string Message
+ {
+ get => _Message;
+ set => SetProperty(ref _Message, value);
+ }
+
+ private string _Icon= $"pack://siteoforigin:,,,/Resources/Images/info.png";
+ public string Icon
+ {
+ get => _Icon;
+ set => SetProperty(ref _Icon, value);
+ }
+
+ private bool _ShowYes;
+ public bool ShowYes
+ {
+ get => _ShowYes;
+ set => SetProperty(ref _ShowYes, value);
+ }
+
+ private bool _ShowNo;
+ public bool ShowNo
+ {
+ get => _ShowNo;
+ set => SetProperty(ref _ShowNo, value);
+ }
+
+ private bool _ShowOk;
+ public bool ShowOk
+ {
+ get => _ShowOk;
+ set => SetProperty(ref _ShowOk, value);
+ }
+
+ private bool _ShowCancel;
+ public bool ShowCancel
+ {
+ get => _ShowCancel;
+ set => SetProperty(ref _ShowCancel, value);
+ }
+
+ #endregion
+
+ #region 命令
+ public ICommand YesCommand { get; set; }
+ public ICommand NoCommand { get; set; }
+ public ICommand OkCommand { get; set; }
+ public ICommand CancelCommand { get; set; }
+ #endregion
+
+ public DialogCloseListener RequestClose { get; set; }
+
+ public MessageBoxViewModel(IContainerProvider containerProvider):base(containerProvider)
+ {
+ YesCommand = new DelegateCommand(OnYes);
+ NoCommand = new DelegateCommand(OnNo);
+ OkCommand = new DelegateCommand(OnOk);
+ CancelCommand = new DelegateCommand(OnCancel);
+ }
+
+ private void CloseDialog(ButtonResult result)
+ {
+ var parameters = new DialogParameters();
+ RequestClose.Invoke(new DialogResult(result));
+ }
+
+ private void OnYes() => CloseDialog(ButtonResult.Yes);
+ private void OnNo() => CloseDialog(ButtonResult.No);
+ private void OnOk() => CloseDialog(ButtonResult.OK);
+ private void OnCancel() => CloseDialog(ButtonResult.Cancel);
+
+ #region Prism Dialog 规范
+ public bool CanCloseDialog() => true;
+
+ public override void OnDialogClosed()
+ {
+ _eventAggregator.GetEvent().Publish(false);
+ }
+
+ public override void OnDialogOpened(IDialogParameters parameters)
+ {
+ _eventAggregator.GetEvent().Publish(true);
+ Title = parameters.GetValue("Title");
+ Message = parameters.GetValue("Message");
+ var iconKey = parameters.GetValue("Icon"); // info / error / warn
+ Icon = iconKey switch
+ {
+ "info" => $"pack://siteoforigin:,,,/Resources/Images/info.png",
+ "error" => $"pack://siteoforigin:,,,/Resources/Images/error.png",
+ "warn" => $"pack://siteoforigin:,,,/Resources/Images/warning.png",
+ _ => $"pack://siteoforigin:,,,/Resources/Images/info.png" // 默认
+ };
+
+
+ ShowYes = parameters.GetValue("ShowYes");
+ ShowNo = parameters.GetValue("ShowNo");
+ ShowOk = parameters.GetValue("ShowOk");
+ ShowCancel = parameters.GetValue("ShowCancel");
+ }
+ #endregion
+ }
+}
diff --git a/BaseFrame/ViewModels/MonitorViewModel.cs b/BaseFrame/ViewModels/MonitorViewModel.cs
new file mode 100644
index 0000000..20cb103
--- /dev/null
+++ b/BaseFrame/ViewModels/MonitorViewModel.cs
@@ -0,0 +1,40 @@
+using BaseFrame.PubEvent;
+using ECCS.ViewModels;
+using NLog;
+using Prism.Dialogs;
+using Prism.Events;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace BaseFrame.ViewModels
+{
+ public class MonitorViewModel : NavigateViewModelBase
+ {
+ #region 属性
+
+
+ #endregion
+ #region 命令
+
+
+
+ #endregion
+ public MonitorViewModel(IContainerProvider containerProvider) : base(containerProvider)
+ {
+
+
+ }
+ #region 命令处理
+
+
+
+ #endregion
+
+
+ }
+}
diff --git a/BaseFrame/ViewModels/NavigateViewModelBase.cs b/BaseFrame/ViewModels/NavigateViewModelBase.cs
new file mode 100644
index 0000000..e149c05
--- /dev/null
+++ b/BaseFrame/ViewModels/NavigateViewModelBase.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ECCS.ViewModels
+{
+ public abstract class NavigateViewModelBase : BindableBase, INavigationAware
+ {
+ public DialogCloseListener RequestClose { get; set; }
+ public IEventAggregator _eventAggregator;
+ public IDialogService _dialogService;
+ public NavigateViewModelBase(IContainerProvider containerProvider)
+ {
+ _eventAggregator = containerProvider.Resolve();
+ _dialogService = containerProvider.Resolve();
+
+ }
+
+ #region Navigation
+
+ public virtual void OnNavigatedTo(NavigationContext navigationContext) { }
+
+ public virtual bool IsNavigationTarget(NavigationContext navigationContext) => true;
+
+ public virtual void OnNavigatedFrom(NavigationContext navigationContext) { }
+
+ #endregion
+ }
+
+}
diff --git a/BaseFrame/ViewModels/SettingViewModel.cs b/BaseFrame/ViewModels/SettingViewModel.cs
new file mode 100644
index 0000000..4c60100
--- /dev/null
+++ b/BaseFrame/ViewModels/SettingViewModel.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ECCS.ViewModels
+{
+ public class SettingViewModel : NavigateViewModelBase
+ {
+ #region 属性
+
+
+ #endregion
+ #region 命令
+
+
+
+ #endregion
+ public SettingViewModel(IContainerProvider containerProvider) : base(containerProvider)
+ {
+
+
+ }
+ #region 命令处理
+
+
+
+ #endregion
+
+ }
+}
diff --git a/BaseFrame/ViewModels/ShellViewModel.cs b/BaseFrame/ViewModels/ShellViewModel.cs
new file mode 100644
index 0000000..24747f8
--- /dev/null
+++ b/BaseFrame/ViewModels/ShellViewModel.cs
@@ -0,0 +1,94 @@
+using BaseFrame.Views;
+
+using MaterialDesignThemes.Wpf;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+
+namespace BaseFrame.ViewModels
+{
+ public class ShellViewModel : BindableBase
+ {
+ #region 属性
+ private bool _IsLeftDrawerOpen;
+
+ public bool IsLeftDrawerOpen
+ {
+ get => _IsLeftDrawerOpen;
+ set => SetProperty(ref _IsLeftDrawerOpen, value);
+ }
+
+ #endregion
+ #region 命令
+ public ICommand LeftDrawerOpenCommand { get; set; }
+ public ICommand MinimizeCommand { get; set; }
+ public ICommand MaximizeCommand { get; set; }
+ public ICommand CloseCommand { get; set; }
+ public ICommand NavigateCommand { get; set; }
+
+ #endregion
+
+ private IEventAggregator _eventAggregator;
+ private IRegionManager _regionManager;
+ public ShellViewModel( IContainerProvider containerProvider)
+ {
+ _eventAggregator = containerProvider.Resolve();
+ _regionManager = containerProvider.Resolve();
+ LeftDrawerOpenCommand = new DelegateCommand(LeftDrawerOpen);
+ MinimizeCommand = new DelegateCommand(MinimizeWindow);
+ MaximizeCommand = new DelegateCommand(MaximizeWindow);
+ CloseCommand = new DelegateCommand(CloseWindow);
+ NavigateCommand = new DelegateCommand(Navigate);
+
+ }
+ #region 命令处理
+ private void Navigate(string content)
+ {
+ switch (content)
+ {
+ case "监控界面":
+ _regionManager.RequestNavigate("ShellViewManager", "MonitorView");
+ break;
+
+ case "设置界面":
+ _regionManager.RequestNavigate("ShellViewManager", "SettingView");
+ break;
+
+ case "更新界面":
+ _regionManager.RequestNavigate("ShellViewManager", "UpdateInfoView");
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ private void LeftDrawerOpen()
+ {
+ IsLeftDrawerOpen = true;
+ }
+ private void MinimizeWindow(Window window)
+ {
+ if (window != null)
+ window.WindowState = WindowState.Minimized;
+ }
+
+ private void MaximizeWindow(Window window)
+ {
+ if (window != null)
+ {
+ window.WindowState = window.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
+ }
+ }
+
+ private void CloseWindow(Window window)
+ {
+ window?.Close();
+ }
+ #endregion
+ }
+}
diff --git a/BaseFrame/ViewModels/UpdateInfoViewModel.cs b/BaseFrame/ViewModels/UpdateInfoViewModel.cs
new file mode 100644
index 0000000..54c3dd2
--- /dev/null
+++ b/BaseFrame/ViewModels/UpdateInfoViewModel.cs
@@ -0,0 +1,36 @@
+using Prism.Dialogs;
+using Prism.Events;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ECCS.ViewModels
+{
+ public class UpdateInfoViewModel : NavigateViewModelBase
+ {
+ #region 属性
+
+
+ #endregion
+ #region 命令
+
+
+
+ #endregion
+
+ public UpdateInfoViewModel(IContainerProvider containerProvider) : base(containerProvider)
+ {
+
+
+ }
+ #region 命令处理
+
+
+
+ #endregion
+
+
+ }
+}
diff --git a/BaseFrame/Views/Dialogs/MessageBoxView.xaml b/BaseFrame/Views/Dialogs/MessageBoxView.xaml
new file mode 100644
index 0000000..820701b
--- /dev/null
+++ b/BaseFrame/Views/Dialogs/MessageBoxView.xaml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BaseFrame/Views/Dialogs/MessageBoxView.xaml.cs b/BaseFrame/Views/Dialogs/MessageBoxView.xaml.cs
new file mode 100644
index 0000000..6a75ba6
--- /dev/null
+++ b/BaseFrame/Views/Dialogs/MessageBoxView.xaml.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace BaseFrame.Views.Dialogs
+{
+ ///
+ /// MessageBoxView.xaml 的交互逻辑
+ ///
+ public partial class MessageBoxView : UserControl
+ {
+ public MessageBoxView()
+ {
+ InitializeComponent();
+ }
+
+ private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.LeftButton == MouseButtonState.Pressed)
+ {
+ Window.GetWindow(this)?.DragMove();
+ }
+ }
+ }
+}
diff --git a/BaseFrame/Views/MonitorView.xaml b/BaseFrame/Views/MonitorView.xaml
new file mode 100644
index 0000000..d704ee9
--- /dev/null
+++ b/BaseFrame/Views/MonitorView.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/BaseFrame/Views/MonitorView.xaml.cs b/BaseFrame/Views/MonitorView.xaml.cs
new file mode 100644
index 0000000..868f5eb
--- /dev/null
+++ b/BaseFrame/Views/MonitorView.xaml.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace BaseFrame.Views
+{
+ ///
+ /// MainView.xaml 的交互逻辑
+ ///
+ public partial class MonitorView : UserControl
+ {
+ public MonitorView()
+ {
+ InitializeComponent();
+ }
+
+
+ }
+}
diff --git a/BaseFrame/Views/SettingView.xaml b/BaseFrame/Views/SettingView.xaml
new file mode 100644
index 0000000..4bd2b85
--- /dev/null
+++ b/BaseFrame/Views/SettingView.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/BaseFrame/Views/SettingView.xaml.cs b/BaseFrame/Views/SettingView.xaml.cs
new file mode 100644
index 0000000..4a1dd3c
--- /dev/null
+++ b/BaseFrame/Views/SettingView.xaml.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace ECCS.Views
+{
+ ///
+ /// SettingView.xaml 的交互逻辑
+ ///
+ public partial class SettingView : UserControl
+ {
+ public SettingView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/BaseFrame/Views/ShellView.xaml b/BaseFrame/Views/ShellView.xaml
new file mode 100644
index 0000000..59035b9
--- /dev/null
+++ b/BaseFrame/Views/ShellView.xaml
@@ -0,0 +1,204 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BaseFrame/Views/ShellView.xaml.cs b/BaseFrame/Views/ShellView.xaml.cs
new file mode 100644
index 0000000..37c458a
--- /dev/null
+++ b/BaseFrame/Views/ShellView.xaml.cs
@@ -0,0 +1,52 @@
+using BaseFrame.PubEvent;
+
+using Prism.Events;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace BaseFrame.Views
+{
+ ///
+ /// ShellView.xaml 的交互逻辑
+ ///
+ public partial class ShellView : Window
+ {
+ public ShellView(IEventAggregator eventAggregator)
+ {
+ InitializeComponent();
+ //注册灰度遮罩层
+ eventAggregator.GetEvent().Subscribe(ShowOverlay);
+ eventAggregator.GetEvent().Subscribe(ShowWaitinglay);
+ }
+
+ private void ShowWaitinglay(bool arg)
+ {
+ Waitinglay.Visibility = arg ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ private void ShowOverlay(bool arg)
+ {
+ Overlay.Visibility = arg ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+
+ private void ColorZone_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.LeftButton == MouseButtonState.Pressed)
+ {
+ Window.GetWindow(this)?.DragMove();
+ }
+ }
+ }
+}
diff --git a/BaseFrame/Views/UpdateInfoView.xaml b/BaseFrame/Views/UpdateInfoView.xaml
new file mode 100644
index 0000000..de997aa
--- /dev/null
+++ b/BaseFrame/Views/UpdateInfoView.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/BaseFrame/Views/UpdateInfoView.xaml.cs b/BaseFrame/Views/UpdateInfoView.xaml.cs
new file mode 100644
index 0000000..883a052
--- /dev/null
+++ b/BaseFrame/Views/UpdateInfoView.xaml.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace ECCS.Views
+{
+ ///
+ /// UpdateInfoView.xaml 的交互逻辑
+ ///
+ public partial class UpdateInfoView : UserControl
+ {
+ public UpdateInfoView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Common/Common.csproj b/Common/Common.csproj
new file mode 100644
index 0000000..d9175bf
--- /dev/null
+++ b/Common/Common.csproj
@@ -0,0 +1,17 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Common/DTOS/FeishuResponse.cs b/Common/DTOS/FeishuResponse.cs
new file mode 100644
index 0000000..69670c6
--- /dev/null
+++ b/Common/DTOS/FeishuResponse.cs
@@ -0,0 +1,160 @@
+using Newtonsoft.Json;
+
+
+namespace Common.DTOS
+{
+ public class FeishuResponse
+ {
+ [JsonProperty("code")]
+ public int Code { get; set; }
+
+ [JsonProperty("msg")]
+ public string Msg { get; set; }
+
+ [JsonProperty("data")]
+ public T Data { get; set; }
+ }
+
+ #region 创建多维表格返回数据
+ public class CreateBitableAppData
+ {
+ [JsonProperty("app")]
+ public AppInfo App { get; set; }
+ }
+
+ public class AppInfo
+ {
+ [JsonProperty("app_token")]
+ public string AppToken { get; set; }
+
+ [JsonProperty("default_table_id")]
+ public string DefaultTableId { get; set; }
+
+ [JsonProperty("folder_token")]
+ public string FolderToken { get; set; }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("url")]
+ public string Url { get; set; }
+ }
+ #endregion
+
+ #region 新增数据表返回数据
+ public class CreateTableData
+ {
+ [JsonProperty("default_view_id")]
+ public string DefaultViewId { get; set; }
+
+ [JsonProperty("field_id_list")]
+ public List FieldIdList { get; set; }
+
+ [JsonProperty("table_id")]
+ public string TableId { get; set; }
+ }
+
+ #endregion
+
+ #region 查询数据表列表返回数据
+
+ public class QueryTableListData
+ {
+ [JsonProperty("has_more")]
+ public bool HasMore { get; set; }
+
+ [JsonProperty("items")]
+ public List Items { get; set; }
+
+ [JsonProperty("page_token")]
+ public string PageToken { get; set; }
+
+ [JsonProperty("total")]
+ public int Total { get; set; }
+ }
+
+ public class TableItem
+ {
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("revision")]
+ public int Revision { get; set; }
+
+ [JsonProperty("table_id")]
+ public string TableId { get; set; }
+ }
+
+ #endregion
+
+ #region 更新或新增记录返回数据
+
+ public class CreateRecordData
+ {
+ [JsonProperty("record")]
+ public RecordInfo Record { get; set; }
+ }
+
+ public class RecordInfo
+ {
+ ///
+ /// 字段数据(字段名 -> 值)
+ ///
+ [JsonProperty("fields")]
+ public Dictionary Fields { get; set; }
+
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonProperty("record_id")]
+ public string RecordId { get; set; }
+ }
+
+ #endregion
+
+ #region 查询记录返回数据
+ public class QueryRecordListData
+ {
+ [JsonProperty("has_more")]
+ public bool HasMore { get; set; }
+
+ [JsonProperty("items")]
+ public List Items { get; set; }
+
+ [JsonProperty("total")]
+ public int Total { get; set; }
+ }
+
+ public class RecordItem
+ {
+ [JsonProperty("record_id")]
+ public string RecordId { get; set; }
+
+ [JsonProperty("fields")]
+ public Dictionary Fields { get; set; }
+ }
+ #endregion
+
+ #region 多行创建返回数据
+ public class CreateRecordsRequest
+ {
+ [JsonProperty("records")]
+ public List Records { get; set; }
+ }
+ public class CreateRecordItem
+ {
+ ///
+ /// 字段数据(字段名 -> 值)
+ ///
+ [JsonProperty("fields")]
+ public Dictionary Fields { get; set; }
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonProperty("record_id")]
+ public string RecordId { get; set; }
+
+ }
+
+ #endregion
+}
diff --git a/ECCS.sln b/ECCS.sln
new file mode 100644
index 0000000..947df38
--- /dev/null
+++ b/ECCS.sln
@@ -0,0 +1,55 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.14.36221.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ECCS", "BaseFrame\ECCS.csproj", "{FC10E4D4-0AA3-487B-B023-F8D2949E45ED}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logger", "Logger\Logger.csproj", "{9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ORM", "ORM\ORM.csproj", "{4DE5DC6C-7121-4EB9-B8A8-90C694F451E2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "Service\Service.csproj", "{D8209B91-D7D0-444B-B569-D3FA74D191DD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{C769E6C6-55E9-40C3-A611-9EFAB101BE6A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FC10E4D4-0AA3-487B-B023-F8D2949E45ED}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6D9764D9-B4DA-43E2-A9D7-40A6C871A6B3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9150C6A9-AE8D-42C9-8B2D-9DD04A3E7E74}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4DE5DC6C-7121-4EB9-B8A8-90C694F451E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4DE5DC6C-7121-4EB9-B8A8-90C694F451E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4DE5DC6C-7121-4EB9-B8A8-90C694F451E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4DE5DC6C-7121-4EB9-B8A8-90C694F451E2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D8209B91-D7D0-444B-B569-D3FA74D191DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D8209B91-D7D0-444B-B569-D3FA74D191DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D8209B91-D7D0-444B-B569-D3FA74D191DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D8209B91-D7D0-444B-B569-D3FA74D191DD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C769E6C6-55E9-40C3-A611-9EFAB101BE6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C769E6C6-55E9-40C3-A611-9EFAB101BE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C769E6C6-55E9-40C3-A611-9EFAB101BE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C769E6C6-55E9-40C3-A611-9EFAB101BE6A}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {22BD9235-6581-454D-97D8-F4E932F80888}
+ EndGlobalSection
+EndGlobal
diff --git a/Logger/Logger.csproj b/Logger/Logger.csproj
new file mode 100644
index 0000000..6b0ee50
--- /dev/null
+++ b/Logger/Logger.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/Logger/LoggerHelper.cs b/Logger/LoggerHelper.cs
new file mode 100644
index 0000000..365cbec
--- /dev/null
+++ b/Logger/LoggerHelper.cs
@@ -0,0 +1,42 @@
+using NLog;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace Logger
+{
+ public static class LoggerHelper
+ {
+ public static readonly ILogger Logger = LogManager.GetLogger("InfoLogger");
+ public static readonly ILogger sqlLogger = LogManager.GetLogger("SqlLogger");
+
+ // 解析堆栈,找到项目文件路径和行号
+ public static string GetProjectStackLine(string stackTrace)
+ {
+ if (string.IsNullOrEmpty(stackTrace))
+ return "未知位置";
+
+ var lines = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (var line in lines)
+ {
+ // 匹配你项目的命名空间路径
+ if (line.Contains("BaseFrame"))
+ {
+ // 提取 "in 文件路径:line 行号"
+ var match = Regex.Match(line, @"in (.+?):line (\d+)");
+ if (match.Success)
+ {
+ return match.Value; // 返回类似 C:\...\MainViewModel.cs:line 37
+ }
+ return line.Trim();
+ }
+ }
+
+ return lines[0].Trim(); // 如果找不到就返回第一条
+ }
+ }
+}
diff --git a/Logger/Nlog.config b/Logger/Nlog.config
new file mode 100644
index 0000000..1b31316
--- /dev/null
+++ b/Logger/Nlog.config
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Model/Model.csproj b/Model/Model.csproj
new file mode 100644
index 0000000..fa71b7a
--- /dev/null
+++ b/Model/Model.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/ORM/DatabaseConfig.cs b/ORM/DatabaseConfig.cs
new file mode 100644
index 0000000..3df55bc
--- /dev/null
+++ b/ORM/DatabaseConfig.cs
@@ -0,0 +1,97 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ORM
+{
+ public static class DatabaseConfig
+ {
+ ///
+ /// 当前数据库类型
+ ///
+ public static DbType DbConnectionType { get; private set; }
+
+ ///
+ /// 数据库连接字符串
+ ///
+ public static string DbConnectionString { get; private set; }
+
+ ///
+ /// 默认 SQLite 文件路径(仅记录,不自动启用)
+ ///
+ public static string DefaultDbFilePath { get; private set; }
+
+ ///
+ /// 租户 Id
+ ///
+ public static int TenantId { get; private set; }
+
+ public static int SnowFlakeDatacenterId { get; private set; }
+ public static int SnowFlakeWorkId { get; private set; }
+
+ static DatabaseConfig()
+ {
+ InitDefaultDbPath(); // 只生成路径,不指定 DB 类型/连接
+ }
+
+ ///
+ /// 生成默认 DB 路径 EXE目录/SQLDB/Data.db
+ ///
+ private static void InitDefaultDbPath()
+ {
+ string baseDir = AppDomain.CurrentDomain.BaseDirectory;
+
+ string folder = Path.Combine(baseDir, "SQLDB");
+ if (!Directory.Exists(folder))
+ Directory.CreateDirectory(folder);
+
+ DefaultDbFilePath = Path.Combine(folder, "Data.db");
+ }
+
+ ///
+ /// 手动设置数据库连接
+ ///
+ public static void SetConnection(string conn, DbType dbType)
+ {
+ if (string.IsNullOrWhiteSpace(conn))
+ throw new Exception("数据库连接字符串为空");
+
+ DbConnectionString = conn;
+ DbConnectionType = dbType;
+ }
+
+ ///
+ /// 如果未 SetConnection,默认使用 SQLite + 默认路径
+ ///
+ public static void EnsureConnection()
+ {
+ if (string.IsNullOrEmpty(DbConnectionString))
+ {
+ DbConnectionString = $"Data Source={DefaultDbFilePath};";
+ DbConnectionType = DbType.Sqlite;
+ }
+ }
+
+ ///
+ /// 设置租户并计算雪花算法节点
+ ///
+ public static void SetTenant(int tenantId)
+ {
+ if (tenantId <= 10000)
+ throw new Exception("数据租户Id值需大于10000");
+
+ TenantId = tenantId;
+
+ int snowId = (tenantId - 10000) % 1024;
+
+ if (snowId is < 0 or > 1023)
+ throw new Exception("雪花算法机器码值范围0至1023");
+
+ SnowFlakeDatacenterId = snowId >> 5;
+ SnowFlakeWorkId = snowId & 31;
+ }
+ }
+}
diff --git a/ORM/ORM.csproj b/ORM/ORM.csproj
new file mode 100644
index 0000000..72f7a86
--- /dev/null
+++ b/ORM/ORM.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Service/APIService.cs b/Service/APIService.cs
new file mode 100644
index 0000000..48ab51d
--- /dev/null
+++ b/Service/APIService.cs
@@ -0,0 +1,388 @@
+using Common.DTOS;
+using NetTaste;
+using RestSharp;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Service
+{
+ public class APIService
+ {
+
+ private static string Token = "Bearer u-cVaGe.hv160U5.zqxeyRKx11ifIR04iPjoayYQC00EXF";
+ #region 表处理
+ ///
+ /// 初始化多维表格
+ ///
+ /// 表格名称
+ public static FeishuResponse InitForm(string FormName)
+ {
+ try
+ {
+ var client = new RestClient("https://open.feishu.cn/open-apis/bitable/v1/apps");
+ client.Timeout = -1;
+ var request = new RestRequest(Method.POST);
+ request.AddHeader("Content-Type", "application/json");
+ request.AddHeader("Authorization", Token);
+ request.AddParameter("application/json", ParameterType.RequestBody);
+ request.AddJsonBody(new
+ {
+ folder_token = "",
+ name = FormName
+ });
+ IRestResponse response = client.Execute(request);
+ return Newtonsoft.Json.JsonConvert.DeserializeObject>(response.Content);
+ }
+ catch (Exception ex)
+ {
+ throw;
+ }
+
+ }
+ ///
+ /// 创建表
+ ///
+ /// 多维表格ID
+ /// 表名称
+ public static FeishuResponse AddTable(string FormID, string TableName)
+ {
+ try
+ {
+ var client = new RestClient($"https://open.feishu.cn/open-apis/bitable/v1/apps/{FormID}/tables");
+ client.Timeout = -1;
+ var request = new RestRequest(Method.POST);
+ request.AddHeader("Content-Type", "application/json");
+ request.AddHeader("Authorization", Token);
+ request.AddJsonBody(new
+ {
+ table = new
+ {
+ default_view_name = "默认的表格视图",
+ name = TableName,
+ fields = new object[]
+ {
+ new
+ {
+ field_name = "索引字段",
+ type = 1
+ },
+ new
+ {
+ field_name = "单选",
+ type = 3,
+ ui_type = "SingleSelect",
+ property = new
+ {
+ options = new[]
+ {
+ new { color = 0, name = "Enabled" },
+ new { color = 1, name = "Disabled" },
+ new { color = 2, name = "Draft" }
+ }
+ }
+ }
+ }
+ }
+ });
+ request.AddParameter("application/json", ParameterType.RequestBody);
+ IRestResponse response = client.Execute(request);
+ return Newtonsoft.Json.JsonConvert.DeserializeObject>(response.Content);
+ }
+ catch (Exception ex)
+ {
+ throw;
+ }
+ }
+ ///
+ /// 删除表
+ ///
+ /// 多维表格ID
+ /// 表ID
+ public static FeishuResponse