增强应用程序异常处理机制,新增未处理异常日志记录功能,确保在启动和运行期间捕获并记录异常信息。同时,重构配置文件加载逻辑,支持用户目录覆盖默认配置,优化 SQLite 数据库连接字符串处理,确保在不同环境下的兼容性和稳定性。
This commit is contained in:
@@ -4,7 +4,9 @@ using Mapster;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using NewLife;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.EventBus;
|
||||
using YY.Admin.Filter;
|
||||
@@ -14,6 +16,7 @@ using YY.Admin.Services.Service.Print;
|
||||
using YY.Admin.Setup;
|
||||
using YY.Admin.ViewModels;
|
||||
using YY.Admin.Views;
|
||||
using YY.Admin.Core.Util;
|
||||
namespace YY.Admin
|
||||
{
|
||||
/// <summary>
|
||||
@@ -24,32 +27,116 @@ namespace YY.Admin
|
||||
private IConfiguration? _configuration;
|
||||
private ILoggerService? _logger;
|
||||
private readonly SyncModule _syncModule = new();
|
||||
|
||||
public App()
|
||||
{
|
||||
DispatcherUnhandledException += OnDispatcherUnhandledException;
|
||||
AppDomain.CurrentDomain.UnhandledException += OnUnhandledDomainException;
|
||||
}
|
||||
|
||||
private static void TryWriteCrashLog(string headline, Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "YY.Admin", "logs");
|
||||
Directory.CreateDirectory(dir);
|
||||
var file = Path.Combine(dir, $"crash-{DateTime.Now:yyyyMMdd-HHmmss}.txt");
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(headline);
|
||||
sb.AppendLine(ex.ToString());
|
||||
File.WriteAllText(file, sb.ToString(), Encoding.UTF8);
|
||||
MessageBox.Show($"程序异常已写入日志:\n{file}\n\n{ex.Message}", "智能制造MES工控", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
catch
|
||||
{
|
||||
MessageBox.Show($"{headline}\n{ex}", "智能制造MES工控", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
TryWriteCrashLog("UI 线程未处理异常", e.Exception);
|
||||
e.Handled = true;
|
||||
Shutdown(1);
|
||||
}
|
||||
|
||||
private void OnUnhandledDomainException(object? sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
if (e.ExceptionObject is Exception ex)
|
||||
{
|
||||
TryWriteCrashLog("域未处理异常", ex);
|
||||
}
|
||||
|
||||
if (e.IsTerminating)
|
||||
{
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Window CreateShell()
|
||||
{
|
||||
return Container.Resolve<LoginWindow>();
|
||||
}
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
// 构建配置
|
||||
_configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(baseDirectory)
|
||||
.AddJsonFile("Configuration/appsettings.json", optional: false, reloadOnChange: true)
|
||||
.Build();
|
||||
// 全局配置
|
||||
TypeAdapterConfig.GlobalSettings.Default
|
||||
.IgnoreNullValues(true)
|
||||
.NameMatchingStrategy(NameMatchingStrategy.IgnoreCase);
|
||||
try
|
||||
{
|
||||
var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
var cfgPath = Path.Combine(baseDirectory, "Configuration", "appsettings.json");
|
||||
if (!File.Exists(cfgPath))
|
||||
{
|
||||
var msg = $"未找到配置文件(请确认与 YY.Admin.exe 同目录存在 Configuration\\appsettings.json):\n{cfgPath}";
|
||||
TryWriteStartupFailure(msg);
|
||||
Shutdown(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// FluentValidation 全局规则级别配置
|
||||
ValidatorOptions.Global.DefaultRuleLevelCascadeMode = CascadeMode.Stop;
|
||||
var userCfgPath = Path.Combine(
|
||||
AppWritablePaths.EnsureDirectoryExists(AppWritablePaths.ConfigurationDirectory),
|
||||
"appsettings.json");
|
||||
|
||||
// Mapster 全局配置
|
||||
#if DEBUG
|
||||
//TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true;
|
||||
#endif
|
||||
// 构建配置:安装目录默认 + 用户目录覆盖(JeecgIntegration 等可由服务器设置写入)
|
||||
_configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(baseDirectory)
|
||||
.AddJsonFile(Path.Combine("Configuration", "appsettings.json"), optional: false, reloadOnChange: true)
|
||||
.AddJsonFile(userCfgPath, optional: true, reloadOnChange: true)
|
||||
.Build();
|
||||
// 全局配置
|
||||
TypeAdapterConfig.GlobalSettings.Default
|
||||
.IgnoreNullValues(true)
|
||||
.NameMatchingStrategy(NameMatchingStrategy.IgnoreCase);
|
||||
|
||||
base.OnStartup(e);
|
||||
// FluentValidation 全局规则级别配置
|
||||
ValidatorOptions.Global.DefaultRuleLevelCascadeMode = CascadeMode.Stop;
|
||||
|
||||
// Mapster 全局配置
|
||||
#if DEBUG
|
||||
//TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true;
|
||||
#endif
|
||||
|
||||
base.OnStartup(e);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TryWriteCrashLog("启动阶段异常(配置/框架初始化失败)", ex);
|
||||
Shutdown(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryWriteStartupFailure(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "YY.Admin", "logs");
|
||||
Directory.CreateDirectory(dir);
|
||||
var file = Path.Combine(dir, $"startup-{DateTime.Now:yyyyMMdd-HHmmss}.txt");
|
||||
File.WriteAllText(file, message, Encoding.UTF8);
|
||||
MessageBox.Show($"{message}\n\n已记录:{file}", "智能制造MES工控", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
catch
|
||||
{
|
||||
MessageBox.Show(message, "智能制造MES工控", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
//注册
|
||||
protected override void RegisterTypes(IContainerRegistry containerRegistry)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.IO;
|
||||
using YY.Admin.Core.Util;
|
||||
|
||||
namespace YY.Admin.Helper
|
||||
{
|
||||
@@ -11,6 +12,37 @@ namespace YY.Admin.Helper
|
||||
{
|
||||
private const string DefaultWebSocketPath = "/websocket/scada-sync";
|
||||
|
||||
/// <summary>
|
||||
/// 安装目录随包发布的默认配置(只读)。
|
||||
/// </summary>
|
||||
public static string GetBundledAppSettingsPath()
|
||||
{
|
||||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configuration", "appsettings.json");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户覆盖配置(可写),仅覆盖 JeecgIntegration 节点时使用。
|
||||
/// </summary>
|
||||
public static string GetUserAppSettingsPath()
|
||||
{
|
||||
var dir = AppWritablePaths.EnsureDirectoryExists(AppWritablePaths.ConfigurationDirectory);
|
||||
return Path.Combine(dir, "appsettings.json");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 兼容旧调用:优先返回用于读取的实际路径(存在用户覆盖则用用户文件)。
|
||||
/// </summary>
|
||||
public static string GetConfigPath()
|
||||
{
|
||||
var user = GetUserAppSettingsPath();
|
||||
if (File.Exists(user))
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
||||
return GetBundledAppSettingsPath();
|
||||
}
|
||||
|
||||
public class ServerSettingsModel
|
||||
{
|
||||
public string Ip { get; set; } = "127.0.0.1";
|
||||
@@ -25,22 +57,35 @@ namespace YY.Admin.Helper
|
||||
public bool DisconnectConnection { get; set; } = false;
|
||||
}
|
||||
|
||||
public static string GetConfigPath()
|
||||
private static JObject LoadMergedRoot()
|
||||
{
|
||||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configuration", "appsettings.json");
|
||||
var bundledPath = GetBundledAppSettingsPath();
|
||||
if (!File.Exists(bundledPath))
|
||||
{
|
||||
throw new FileNotFoundException("未找到安装目录默认配置文件 appsettings.json", bundledPath);
|
||||
}
|
||||
|
||||
var root = JObject.Parse(File.ReadAllText(bundledPath));
|
||||
var userPath = GetUserAppSettingsPath();
|
||||
if (!File.Exists(userPath))
|
||||
{
|
||||
return root;
|
||||
}
|
||||
|
||||
var userRoot = JObject.Parse(File.ReadAllText(userPath));
|
||||
var userJeecg = userRoot["JeecgIntegration"] as JObject;
|
||||
if (userJeecg != null)
|
||||
{
|
||||
root["JeecgIntegration"] = userJeecg;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
public static ServerSettingsModel Load()
|
||||
{
|
||||
var model = new ServerSettingsModel();
|
||||
var path = GetConfigPath();
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return model;
|
||||
}
|
||||
|
||||
var content = File.ReadAllText(path);
|
||||
var root = JObject.Parse(content);
|
||||
var root = LoadMergedRoot();
|
||||
var jeecg = root["JeecgIntegration"] as JObject;
|
||||
if (jeecg == null)
|
||||
{
|
||||
@@ -64,14 +109,7 @@ namespace YY.Admin.Helper
|
||||
|
||||
public static void Save(ServerSettingsModel model)
|
||||
{
|
||||
var path = GetConfigPath();
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
throw new FileNotFoundException("未找到配置文件 appsettings.json", path);
|
||||
}
|
||||
|
||||
var content = File.ReadAllText(path);
|
||||
var root = JObject.Parse(content);
|
||||
var root = LoadMergedRoot();
|
||||
var jeecg = root["JeecgIntegration"] as JObject;
|
||||
if (jeecg == null)
|
||||
{
|
||||
@@ -95,7 +133,9 @@ namespace YY.Admin.Helper
|
||||
jeecg["WebSocketPath"] = webSocketPath;
|
||||
jeecg["DisconnectConnection"] = model.DisconnectConnection;
|
||||
|
||||
File.WriteAllText(path, root.ToString(Formatting.Indented));
|
||||
var userPath = GetUserAppSettingsPath();
|
||||
var outRoot = new JObject { ["JeecgIntegration"] = jeecg };
|
||||
File.WriteAllText(userPath, outRoot.ToString(Formatting.Indented));
|
||||
}
|
||||
|
||||
public static string BuildDefaultWebSocketUrl(string baseScheme, string ip, int port, string basePath, string webSocketPath = DefaultWebSocketPath)
|
||||
@@ -106,6 +146,7 @@ namespace YY.Admin.Helper
|
||||
{
|
||||
safeBasePath = "/" + safeBasePath;
|
||||
}
|
||||
|
||||
var safeWsPath = NormalizeWebSocketPath(webSocketPath);
|
||||
return $"{safeScheme}://{ip}:{port}{safeBasePath}{safeWsPath}";
|
||||
}
|
||||
@@ -117,6 +158,7 @@ namespace YY.Admin.Helper
|
||||
{
|
||||
value = "/" + value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
23
yy-admin-master/YY.Admin/Helper/WebView2UserDataFolder.cs
Normal file
23
yy-admin-master/YY.Admin/Helper/WebView2UserDataFolder.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.IO;
|
||||
using Microsoft.Web.WebView2.Wpf;
|
||||
using YY.Admin.Core.Util;
|
||||
|
||||
namespace YY.Admin.Helper;
|
||||
|
||||
/// <summary>
|
||||
/// WebView2 默认将用户数据目录放在宿主 exe 旁边;安装在 Program Files 时目录只读会导致初始化失败、预览白屏。
|
||||
/// 统一到当前用户 LocalAppData 下的可写路径。
|
||||
/// </summary>
|
||||
public static class WebView2UserDataFolder
|
||||
{
|
||||
/// <summary>
|
||||
/// 为控件创建 CreationProperties(须在首次 EnsureCoreWebView2Async 之前赋值)。
|
||||
/// </summary>
|
||||
/// <param name="subFolder">子目录名,避免不同场景争用同一 profile。</param>
|
||||
public static CoreWebView2CreationProperties CreateCreationProperties(string subFolder)
|
||||
{
|
||||
var folder = AppWritablePaths.EnsureDirectoryExists(
|
||||
Path.Combine(AppWritablePaths.LocalApplicationRoot, "WebView2", subFolder));
|
||||
return new CoreWebView2CreationProperties { UserDataFolder = folder };
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Text;
|
||||
using System.Windows;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Microsoft.Web.WebView2.Wpf;
|
||||
using YY.Admin.Core.Util;
|
||||
|
||||
namespace YY.Admin.Infrastructure.Print;
|
||||
|
||||
@@ -42,7 +43,9 @@ public static class HtmlToPdfRenderer
|
||||
{
|
||||
File.WriteAllText(tempHtml, html, Encoding.UTF8);
|
||||
|
||||
var env = await CoreWebView2Environment.CreateAsync();
|
||||
var userData = AppWritablePaths.EnsureDirectoryExists(
|
||||
Path.Combine(AppWritablePaths.LocalApplicationRoot, "WebView2", "HtmlToPdf"));
|
||||
var env = await CoreWebView2Environment.CreateAsync(browserExecutableFolder: null, userDataFolder: userData);
|
||||
var wv = new WebView2();
|
||||
|
||||
win = new Window
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using Prism.Dialogs;
|
||||
using YY.Admin.ViewModels.Control;
|
||||
using YY.Admin.ViewModels.Dialogs;
|
||||
using YY.Admin.Views;
|
||||
@@ -20,6 +20,15 @@ using YY.Admin.Views.Print;
|
||||
|
||||
namespace YY.Admin
|
||||
{
|
||||
/// <summary>
|
||||
/// Prism DialogService 中 <see cref="KnownDialogParameters.WindowName"/> 使用的宿主窗口注册名。
|
||||
/// </summary>
|
||||
public static class DialogWindowNames
|
||||
{
|
||||
/// <summary>标准边框、可调整大小,且不使用 AllowsTransparency(服务器设置等需改 WindowStyle 的对话框)。</summary>
|
||||
public const string ChromeDialogWindow = "ChromeDialogWindow";
|
||||
}
|
||||
|
||||
public static class NavigationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -35,8 +44,9 @@ namespace YY.Admin
|
||||
containerRegistry.RegisterDialog<ConfirmDialogView, ConfirmDialogViewModel>("ConfirmDialog");
|
||||
containerRegistry.RegisterDialog<ServerSettingsDialogView, ServerSettingsDialogViewModel>("ServerSettingsDialog");
|
||||
|
||||
// 设置对话框样式
|
||||
// 默认透明无边框宿主;需调整 WindowStyle/AllowsTransparency 的对话框改用命名宿主 ChromeDialogWindow
|
||||
containerRegistry.RegisterDialogWindow<DialogWindow>();
|
||||
containerRegistry.RegisterDialogWindow<ChromeDialogWindow>(DialogWindowNames.ChromeDialogWindow);
|
||||
|
||||
// 注册导航
|
||||
containerRegistry.RegisterForNavigation<DashboardView>("DashboardView");
|
||||
@@ -103,6 +113,30 @@ namespace YY.Admin
|
||||
}
|
||||
public IDialogResult? Result { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标准窗口边框宿主:从构造起即 AllowsTransparency=false,避免窗口显示后再切换透明属性引发异常。
|
||||
/// </summary>
|
||||
public class ChromeDialogWindow : Window, IDialogWindow
|
||||
{
|
||||
public ChromeDialogWindow()
|
||||
{
|
||||
AllowsTransparency = false;
|
||||
Background = SystemColors.WindowBrush;
|
||||
WindowStyle = WindowStyle.SingleBorderWindow;
|
||||
ResizeMode = ResizeMode.CanResizeWithGrip;
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
// 按内容测量客户区高度;勿将 Window.Height 设为与 UserControl 相同数值,
|
||||
// 否则会与标题栏/边框抢高度导致底部按钮被裁切。
|
||||
SizeToContent = SizeToContent.WidthAndHeight;
|
||||
MinWidth = 520;
|
||||
MinHeight = 360;
|
||||
Title = "对话框";
|
||||
}
|
||||
|
||||
public IDialogResult? Result { get; set; }
|
||||
}
|
||||
|
||||
//public class DialogWindow : Window, IDialogWindow
|
||||
//{
|
||||
// public DialogWindow()
|
||||
|
||||
@@ -11,6 +11,7 @@ using YY.Admin.Core.EventBus;
|
||||
using YY.Admin.Core.Helper;
|
||||
using YY.Admin.Core.Model;
|
||||
using YY.Admin.Core.Session;
|
||||
using YY.Admin.Core.Util;
|
||||
using HcSkinType = HandyControl.Data.SkinType;
|
||||
|
||||
namespace YY.Admin.ViewModels
|
||||
@@ -167,7 +168,10 @@ namespace YY.Admin.ViewModels
|
||||
public static string GetFilePath()
|
||||
{
|
||||
string filePathSuffix = string.Format(CommonConst.AppSettingsFilePath, AppSession.CurrentUser!.Account);
|
||||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filePathSuffix);
|
||||
var fullPath = Path.Combine(AppWritablePaths.LocalApplicationRoot, filePathSuffix);
|
||||
var dir = Path.GetDirectoryName(fullPath);
|
||||
AppWritablePaths.EnsureDirectoryExists(dir!);
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,6 +2,8 @@ using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Prism.Dialogs;
|
||||
using YY.Admin;
|
||||
using YY.Admin.Core.Helper;
|
||||
using YY.Admin.Core.Session;
|
||||
using YY.Admin.FluentValidation;
|
||||
@@ -291,7 +293,11 @@ namespace YY.Admin.ViewModels
|
||||
|
||||
private void OpenServerSettings()
|
||||
{
|
||||
_dialogService.ShowDialog("ServerSettingsDialog", r =>
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ KnownDialogParameters.WindowName, DialogWindowNames.ChromeDialogWindow },
|
||||
};
|
||||
_dialogService.ShowDialog("ServerSettingsDialog", parameters, r =>
|
||||
{
|
||||
if (r.Result == ButtonResult.OK)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Mapster;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Prism.Dialogs;
|
||||
using Newtonsoft.Json;
|
||||
using SqlSugar;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -9,6 +10,7 @@ using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using YY.Admin;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Core.Const;
|
||||
using YY.Admin.Core.Model;
|
||||
@@ -16,12 +18,12 @@ using YY.Admin.Core.Services;
|
||||
using YY.Admin.Core.Session;
|
||||
using YY.Admin.Core.Util;
|
||||
using YY.Admin.Event;
|
||||
using YY.Admin.Helper;
|
||||
using YY.Admin.Module;
|
||||
using YY.Admin.Services.Service.Auth;
|
||||
using YY.Admin.Services.Service.Jeecg;
|
||||
using YY.Admin.Services.Service.Menu;
|
||||
using YY.Admin.ViewModels.Control;
|
||||
using YY.Admin.Helper;
|
||||
|
||||
namespace YY.Admin.ViewModels
|
||||
{
|
||||
@@ -724,7 +726,11 @@ namespace YY.Admin.ViewModels
|
||||
|
||||
private void OpenServerSettings()
|
||||
{
|
||||
_dialogService.ShowDialog("ServerSettingsDialog", r =>
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ KnownDialogParameters.WindowName, DialogWindowNames.ChromeDialogWindow },
|
||||
};
|
||||
_dialogService.ShowDialog("ServerSettingsDialog", parameters, r =>
|
||||
{
|
||||
if (r.Result == ButtonResult.OK)
|
||||
{
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ctls="clr-namespace:YY.Admin.Core.Controls;assembly=YY.Admin.Core"
|
||||
Width="520" Height="420"
|
||||
Loaded="UserControl_Loaded">
|
||||
MinWidth="520">
|
||||
<Border Background="White" BorderBrush="#f0f0f0" BorderThickness="1" CornerRadius="4">
|
||||
<Grid Margin="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<!-- Auto:按表单实际高度排列,避免固定窗体高度时中间行被压扁裁切 -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Windows.Controls;
|
||||
using System.Windows;
|
||||
|
||||
namespace YY.Admin.Views.Dialogs
|
||||
{
|
||||
@@ -12,22 +11,5 @@ namespace YY.Admin.Views.Dialogs
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var win = Window.GetWindow(this);
|
||||
if (win == null) return;
|
||||
|
||||
// 必须先关闭 AllowsTransparency,再改 WindowStyle(否则会抛出:
|
||||
// 「当 AllowsTransparency 为 true 时,WindowStyle.None 是唯一有效值」)
|
||||
win.AllowsTransparency = false;
|
||||
win.WindowStyle = WindowStyle.SingleBorderWindow;
|
||||
win.ResizeMode = ResizeMode.CanResizeWithGrip;
|
||||
win.SizeToContent = SizeToContent.Manual;
|
||||
win.MinWidth = 520;
|
||||
win.MinHeight = 420;
|
||||
if (win.Width < 520) win.Width = 520;
|
||||
if (win.Height < 420) win.Height = 420;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using YY.Admin.Core.Entity;
|
||||
using YY.Admin.Core.Services;
|
||||
using YY.Admin.Helper;
|
||||
using YY.Admin.Services.Service.Print;
|
||||
|
||||
namespace YY.Admin.Views.Print;
|
||||
@@ -32,6 +33,8 @@ public partial class PrintPreviewWindow : HandyControl.Controls.Window
|
||||
_printDotService = printDotService;
|
||||
_initialPrinterName = selectedPrinterName;
|
||||
|
||||
WebView.CreationProperties = WebView2UserDataFolder.CreateCreationProperties("PrintPreview");
|
||||
|
||||
TbTemplateName.Text = template.TemplateName ?? "(未命名)";
|
||||
TbTemplateCode.Text = $"编码:{template.TemplateCode} " +
|
||||
$"尺寸:{template.PaperWidthMm ?? 210}×{template.PaperHeightMm ?? 297} mm " +
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using YY.Admin.Core.Services;
|
||||
using YY.Admin.Helper;
|
||||
using YY.Admin.ViewModels.RawMaterialEntry;
|
||||
|
||||
namespace YY.Admin.Views.RawMaterialEntry;
|
||||
@@ -101,6 +102,7 @@ public partial class RawMaterialCardGenerateConfirmWindow : HandyControl.Control
|
||||
Func<RawMaterialCardGeneratePlanRow, string> previewHtmlBuilder)
|
||||
{
|
||||
InitializeComponent();
|
||||
PreviewWebView.CreationProperties = WebView2UserDataFolder.CreateCreationProperties("RawMaterialCardConfirm");
|
||||
_printDotService = printDotService;
|
||||
_previewHtmlBuilder = previewHtmlBuilder;
|
||||
HeaderText = $"共 {planItems.Count} 张,左侧展示即将生成的原材料卡片,右侧展示业务关联打印模板预览。";
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using YY.Admin.Helper;
|
||||
using YY.Admin.ViewModels.RawMaterialEntry;
|
||||
|
||||
namespace YY.Admin.Views.RawMaterialEntry;
|
||||
@@ -14,6 +15,7 @@ public partial class RawMaterialEntryOperationView : UserControl
|
||||
public RawMaterialEntryOperationView()
|
||||
{
|
||||
InitializeComponent();
|
||||
PrintPreviewWebView.CreationProperties = WebView2UserDataFolder.CreateCreationProperties("RawMaterialEntryPreview");
|
||||
Loaded += OnLoaded;
|
||||
DataContextChanged += OnDataContextChanged;
|
||||
Unloaded += (_, _) => DetachVm();
|
||||
|
||||
@@ -59,6 +59,8 @@
|
||||
<ItemGroup>
|
||||
<Reference Include="HandyControl">
|
||||
<HintPath>..\YY.Admin.Core\libs\HandyControl.dll</HintPath>
|
||||
<!-- 单文件宿主下外部 UI 库需外置,否则易出现 pack:// 资源加载失败(启动即退出) -->
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -106,10 +108,12 @@
|
||||
</None>
|
||||
<None Update="Updates\version.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<!-- 单文件发布时须外置,否则安装目录无物理文件 -->
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- 主程序 BaseDirectory 需含 Configuration/appsettings.json;带 RuntimeIdentifier 时子项目 None 未必进入主输出 -->
|
||||
<!-- 主程序须携带 Configuration/appsettings.json。.NET 单文件发布时 Linked None 的 ExcludeFromSingleFile 可能仍不落盘,故 Publish 后再强制复制一次 -->
|
||||
<ItemGroup>
|
||||
<None Include="..\YY.Admin.Services\Configuration\appsettings.json">
|
||||
<Link>Configuration\appsettings.json</Link>
|
||||
@@ -117,4 +121,13 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="EnsureAppSettingsJsonInPublishDirectory" AfterTargets="Publish">
|
||||
<PropertyGroup>
|
||||
<_AppSettingsSource>$(MSBuildProjectDirectory)\..\YY.Admin.Services\Configuration\appsettings.json</_AppSettingsSource>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(_AppSettingsSource)')" Text="缺少源文件 $(_AppSettingsSource),无法发布 Configuration\appsettings.json。" />
|
||||
<MakeDir Directories="$(PublishDir)Configuration" />
|
||||
<Copy SourceFiles="$(_AppSettingsSource)" DestinationFiles="$(PublishDir)Configuration\appsettings.json" SkipUnchangedFiles="false" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user