diff --git a/Form1.cs b/Form1.cs index 730a6b5..9dc0adf 100644 --- a/Form1.cs +++ b/Form1.cs @@ -40,6 +40,8 @@ namespace Vmianqian private WinPanel pageAlipay = null!; private WinPanel pageSettings = null!; + private AntdUI.Label lblSummaryTitle = null!; + private AntdUI.Label lblSummaryDesc = null!; private AntdUI.Label lblTopNotice = null!; private AntdUI.Label lblWechatStatusValue = null!; private AntdUI.Label lblAlipayStatusValue = null!; @@ -47,8 +49,15 @@ namespace Vmianqian private AntdUI.Input txtServerUrl = null!; private AntdUI.Input txtApiKey = null!; private AntdUI.Input txtNotifyEmail = null!; + private AntdUI.Label lblServerUrlTitle = null!; + private AntdUI.Label lblApiKeyTitle = null!; + private AntdUI.Label lblNotifyEmailTitle = null!; + private AntdUI.Label lblHeartbeatTitle = null!; + private AntdUI.Label lblHeartbeatDesc = null!; + private AntdUI.Label lblMemberPlaceholder = null!; private AntdUI.Switch chkHeartbeatEnabled = null!; private AntdUI.Button btnHeartbeatCheck = null!; + private AntdUI.Button btnClearLog = null!; private WinTextBox txtLog = null!; private AntdUI.Input txtWechatPath = null!; @@ -84,6 +93,11 @@ namespace Vmianqian BackColor = Color.White; Font = new Font("Microsoft YaHei UI", 9F); Text = "V免签 Demo"; + MinimumSize = new Size(1180, 860); + if (Width < 1280 || Height < 900) + { + Size = new Size(1280, 900); + } BuildTitlebar(); BuildBottomBar(); @@ -109,15 +123,6 @@ namespace Vmianqian SubText = "Demo" }; - lblTopNotice = new AntdUI.Label - { - Dock = DockStyle.Right, - Width = 260, - Text = "准备接入 V免签服务端通信", - TextAlign = ContentAlignment.MiddleRight, - ForeColor = Color.FromArgb(22, 119, 255) - }; - lblAlipayStatusValue = new AntdUI.Label { Dock = DockStyle.Right, @@ -241,28 +246,123 @@ namespace Vmianqian private WinPanel CreatePagePanel() { - return new WinPanel + var panel = new WinPanel { Dock = DockStyle.Fill, BackColor = Color.FromArgb(245, 247, 250), AutoScroll = true }; + panel.HorizontalScroll.Enabled = false; + panel.HorizontalScroll.Visible = false; + return panel; } private void LayoutHomePage() { - LayoutPageCards(pageHome, 20); + const int margin = 20; + const int gap = 16; + const int cardHeight = 360; + var pageAvailableWidth = Math.Max(0, pageHome.ClientSize.Width - margin * 2); + pageHome.SuspendLayout(); - var logCard = FindCard(pageHome, "home-log"); - if (logCard != null) + var summaryCard = FindCard(pageHome, "home-summary"); + var top = 160; + try { - var bottom = pageHome.ClientSize.Height - 20; - logCard.Height = Math.Max(220, bottom - logCard.Top); - - if (txtLog != null) + if (summaryCard != null) { - txtLog.Size = new Size(Math.Max(240, logCard.ClientSize.Width - 50), Math.Max(120, logCard.ClientSize.Height - 76)); + summaryCard.SuspendLayout(); + summaryCard.Bounds = new Rectangle(margin, 20, pageAvailableWidth, 120); + lblSummaryTitle.Location = new Point(28, 24); + lblSummaryDesc.Location = new Point(30, 64); + lblSummaryDesc.Size = new Size(Math.Max(220, pageAvailableWidth - 60), 28); + top = summaryCard.Bottom + margin; + summaryCard.ResumeLayout(false); } + + var configCard = FindCard(pageHome, "home-config"); + var memberCard = FindCard(pageHome, "home-member"); + if (configCard != null && memberCard != null) + { + configCard.SuspendLayout(); + memberCard.SuspendLayout(); + + var availableWidth = Math.Max(0, pageAvailableWidth); + var rowBottom = top + cardHeight; + var rowWidth = Math.Max(0, availableWidth - gap); + var leftWidth = rowWidth / 2; + var rightWidth = rowWidth - leftWidth; + + configCard.Bounds = new Rectangle(margin, top, leftWidth, cardHeight); + memberCard.Bounds = new Rectangle(margin + leftWidth + gap, top, rightWidth, cardHeight); + + const int contentLeft = 24; + const int contentRight = 24; + var contentWidth = Math.Max(220, configCard.ClientSize.Width - contentLeft - contentRight); + var inputWidth = contentWidth; + lblServerUrlTitle.Location = new Point(contentLeft, 20); + txtServerUrl.Location = new Point(contentLeft, 48); + txtServerUrl.Width = inputWidth; + lblApiKeyTitle.Location = new Point(contentLeft, 94); + txtApiKey.Location = new Point(contentLeft, 122); + txtApiKey.Width = inputWidth; + lblNotifyEmailTitle.Location = new Point(contentLeft, 168); + txtNotifyEmail.Location = new Point(contentLeft, 196); + txtNotifyEmail.Width = inputWidth; + + const int actionTop = 252; + const int buttonGap = 12; + var actionButtonWidth = Math.Max(72, (contentWidth - buttonGap * 3) / 4); + btnSaveConfig.Location = new Point(24, actionTop); + btnSaveConfig.Size = new Size(actionButtonWidth, 36); + btnStartService.Location = new Point(btnSaveConfig.Right + buttonGap, actionTop); + btnStartService.Size = new Size(actionButtonWidth, 36); + btnStopService.Location = new Point(btnStartService.Right + buttonGap, actionTop); + btnStopService.Size = new Size(actionButtonWidth, 36); + btnHeartbeatCheck.Location = new Point(btnStopService.Right + buttonGap, actionTop); + btnHeartbeatCheck.Size = new Size(actionButtonWidth, 34); + + const int heartbeatTop = 300; + lblHeartbeatTitle.Location = new Point(contentLeft, heartbeatTop); + chkHeartbeatEnabled.Location = new Point(98, heartbeatTop - 4); + lblHeartbeatDesc.Location = new Point(chkHeartbeatEnabled.Right + 4, heartbeatTop + 1); + lblMemberPlaceholder.Size = new Size(Math.Max(220, memberCard.ClientSize.Width - 48), 52); + + var logCardTop = rowBottom + margin; + var homeLogCard = FindCard(pageHome, "home-log"); + if (homeLogCard != null) + { + homeLogCard.Top = logCardTop; + } + + configCard.ResumeLayout(false); + memberCard.ResumeLayout(false); + } + + var logCard = FindCard(pageHome, "home-log"); + if (logCard != null) + { + logCard.SuspendLayout(); + logCard.Left = margin; + logCard.Width = Math.Max(0, pageHome.ClientSize.Width - margin * 2); + + var bottom = pageHome.ClientSize.Height - 20; + logCard.Height = Math.Max(220, bottom - logCard.Top); + + if (txtLog != null) + { + txtLog.Size = new Size(Math.Max(240, logCard.ClientSize.Width - 50), Math.Max(120, logCard.ClientSize.Height - 76)); + } + if (btnClearLog != null) + { + btnClearLog.Location = new Point(Math.Max(24, logCard.ClientSize.Width - btnClearLog.Width - 24), 14); + } + logCard.ResumeLayout(false); + } + } + finally + { + pageHome.ResumeLayout(false); } } @@ -307,7 +407,7 @@ namespace Vmianqian private static void LayoutPageCards(WinPanel page, int margin) { - var targetWidth = Math.Max(320, page.ClientSize.Width - margin * 2); + var targetWidth = Math.Max(0, page.ClientSize.Width - margin * 2); foreach (Control control in page.Controls) { @@ -333,9 +433,8 @@ namespace Vmianqian { var summaryCard = CreateCardPanel(new Rectangle(20, 20, 1080, 120)); summaryCard.Tag = "home-summary"; - summaryCard.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; - var summaryTitle = new AntdUI.Label + lblSummaryTitle = new AntdUI.Label { Text = "欢迎使用 V免签 PC 监听客户端", Font = new Font("Microsoft YaHei UI", 22F, FontStyle.Regular), @@ -343,7 +442,7 @@ namespace Vmianqian Location = new Point(28, 24) }; - var summaryDesc = new AntdUI.Label + lblSummaryDesc = new AntdUI.Label { Text = "当前阶段先打通“PC端本地监听 -> V免签服务端回调 -> 心跳检测”链路,整体布局按 AntdUI Demo 风格复刻。", AutoSize = false, @@ -352,29 +451,32 @@ namespace Vmianqian ForeColor = Color.DimGray }; - summaryCard.Controls.Add(summaryTitle); - summaryCard.Controls.Add(summaryDesc); + summaryCard.Controls.Add(lblSummaryTitle); + summaryCard.Controls.Add(lblSummaryDesc); - var configCard = CreateCardPanel(new Rectangle(20, 160, 1080, 250)); + var configCard = CreateCardPanel(new Rectangle(20, 160, 1080, 360)); configCard.Tag = "home-config"; - configCard.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; - configCard.Controls.Add(CreateTitleLabel("V免签回调地址", 24, 20)); - txtServerUrl = CreateInput(24, 48, 470, "例如:https://你的域名/pay/notify"); + lblServerUrlTitle = CreateTitleLabel("V免签地址", 24, 20); + configCard.Controls.Add(lblServerUrlTitle); + txtServerUrl = CreateInput(24, 48, 500, "例如:https://你的域名/"); configCard.Controls.Add(txtServerUrl); - configCard.Controls.Add(CreateTitleLabel("通信密钥 / Token", 530, 20)); - txtApiKey = CreateInput(530, 48, 350, "请输入与服务端约定的密钥"); + lblApiKeyTitle = CreateTitleLabel("通信密钥 / Token", 24, 94); + configCard.Controls.Add(lblApiKeyTitle); + txtApiKey = CreateInput(24, 122, 500, "请输入与服务端密钥"); configCard.Controls.Add(txtApiKey); - configCard.Controls.Add(CreateTitleLabel("通知邮箱", 24, 102)); - txtNotifyEmail = CreateInput(24, 130, 260, "可选"); + lblNotifyEmailTitle = CreateTitleLabel("通知邮箱", 24, 168); + configCard.Controls.Add(lblNotifyEmailTitle); + txtNotifyEmail = CreateInput(24, 196, 500, "可选"); configCard.Controls.Add(txtNotifyEmail); btnSaveConfig = new AntdUI.Button { Text = "保存配置", - Location = new Point(530, 128), + Type = TTypeMini.Primary, + Location = new Point(24, 252), Size = new Size(110, 36), }; btnSaveConfig.Click += btnSaveConfig_Click; @@ -382,7 +484,8 @@ namespace Vmianqian btnStartService = new AntdUI.Button { Text = "启动监听", - Location = new Point(660, 128), + Type = TTypeMini.Primary, + Location = new Point(146, 252), Size = new Size(110, 36), }; btnStartService.Click += btnStart_Click; @@ -390,7 +493,8 @@ namespace Vmianqian btnStopService = new AntdUI.Button { Text = "停止监听", - Location = new Point(790, 128), + Type = TTypeMini.Primary, + Location = new Point(268, 252), Size = new Size(110, 36), }; btnStopService.Click += btnStop_Click; @@ -399,40 +503,65 @@ namespace Vmianqian configCard.Controls.Add(btnStartService); configCard.Controls.Add(btnStopService); - configCard.Controls.Add(CreateTitleLabel("心跳检测", 24, 188)); + lblHeartbeatTitle = CreateTitleLabel("心跳检测", 24, 300); + configCard.Controls.Add(lblHeartbeatTitle); chkHeartbeatEnabled = new AntdUI.Switch { - Location = new Point(24, 214), + Location = new Point(98, 296), Size = new Size(62, 28), AutoSize = false }; chkHeartbeatEnabled.CheckedChanged += chkHeartbeatEnabled_CheckedChanged; - var heartbeatDesc = new AntdUI.Label + lblHeartbeatDesc = new AntdUI.Label { - Text = "开启后后台每 30 秒自动检测一次,自动成功不写日志,失败才写。", + Text = "自动心跳", AutoSize = true, - Location = new Point(104, 219), + Location = new Point(164, 301), ForeColor = Color.DimGray }; btnHeartbeatCheck = new AntdUI.Button { Text = "立即检测", - Location = new Point(530, 210), + Type = TTypeMini.Primary, + Location = new Point(390, 292), Size = new Size(110, 34) }; btnHeartbeatCheck.Click += btnHeartbeatCheck_Click; configCard.Controls.Add(chkHeartbeatEnabled); - configCard.Controls.Add(heartbeatDesc); + configCard.Controls.Add(lblHeartbeatDesc); configCard.Controls.Add(btnHeartbeatCheck); - var logCard = CreateCardPanel(new Rectangle(20, 430, 1080, 300)); + var memberCard = CreateCardPanel(new Rectangle(660, 160, 440, 360)); + memberCard.Tag = "home-member"; + memberCard.Controls.Add(CreateTitleLabel("会员登录绑定", 24, 20)); + lblMemberPlaceholder = new AntdUI.Label + { + Text = "预留区域:后续用于会员登录、设备绑定、解绑与状态展示。", + AutoSize = false, + Location = new Point(24, 56), + Size = new Size(392, 52), + ForeColor = Color.DimGray + }; + memberCard.Controls.Add(lblMemberPlaceholder); + + var logCard = CreateCardPanel(new Rectangle(20, 500, 1080, 300)); logCard.Tag = "home-log"; - logCard.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; logCard.Controls.Add(CreateTitleLabel("运行日志", 24, 18)); + btnClearLog = new AntdUI.Button + { + Text = "清空", + Type = TTypeMini.Primary, + Ghost = true, + Location = new Point(980, 14), + Size = new Size(72, 30), + Anchor = AnchorStyles.Top | AnchorStyles.Right + }; + btnClearLog.Click += (_, _) => txtLog.Clear(); + logCard.Controls.Add(btnClearLog); txtLog = new WinTextBox { @@ -450,6 +579,7 @@ namespace Vmianqian pageHome.Controls.Add(summaryCard); pageHome.Controls.Add(configCard); + pageHome.Controls.Add(memberCard); pageHome.Controls.Add(logCard); } @@ -598,7 +728,7 @@ namespace Vmianqian { Bounds = bounds, BackColor = Color.White, - BorderStyle = BorderStyle.FixedSingle + BorderStyle = BorderStyle.None }; } @@ -1178,7 +1308,8 @@ namespace Vmianqian var lastPayText = FormatUnixTimestamp(heartbeatResponse.Data.LastPay); var lastHeartText = FormatUnixTimestamp(heartbeatResponse.Data.LastHeart); - if (heartbeatResponse.Data.JkState == 1) + var monitorState = heartbeatResponse.Data.State ?? heartbeatResponse.Data.JkState; + if (monitorState == 1) { if (writeSuccessLog) { @@ -1188,11 +1319,11 @@ namespace Vmianqian return; } - var stateText = heartbeatResponse.Data.JkState switch + var stateText = monitorState switch { 0 => "掉线", -1 => "未绑定监控端", - _ => $"未知({heartbeatResponse.Data.JkState?.ToString() ?? "null"})" + _ => $"未知({monitorState?.ToString() ?? "null"})" }; Log($"心跳检测异常:状态={stateText},最后支付={lastPayText},最后心跳={lastHeartText},返回JSON={heartbeatCheckResult.ResponseBody}"); @@ -1283,7 +1414,7 @@ namespace Vmianqian { lblTopNotice.Text = isRunning ? $"监听中:{BuildLocalListenUrl(_config)}" - : "准备接入 V免签服务端通信"; + : "监听未启动"; } private static void ValidateConfig(ClientConfig config) @@ -1418,8 +1549,8 @@ namespace Vmianqian public sealed class ClientConfig { - public string ServerUrl { get; set; } = "https://zf.yunzer.cn/7eed756ca37ca057370ea9bb208d25fa"; - public string ApiKey { get; set; } = "7eed756ca37ca057370ea9bb208d25fa"; + public string ServerUrl { get; set; } = string.Empty; + public string ApiKey { get; set; } = string.Empty; public string NotifyEmail { get; set; } = string.Empty; public string WechatPath { get; set; } = string.Empty; public string WechatSid { get; set; } = string.Empty; @@ -1491,6 +1622,10 @@ namespace Vmianqian [JsonPropertyName("lastheart")] public long? LastHeart { get; set; } + [JsonPropertyName("state")] + [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)] + public int? State { get; set; } + [JsonPropertyName("jkstate")] [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)] public int? JkState { get; set; } diff --git a/bin/Debug/net10.0-windows/Vmianqian.dll b/bin/Debug/net10.0-windows/Vmianqian.dll index 9d01bb5..1b359b3 100644 Binary files a/bin/Debug/net10.0-windows/Vmianqian.dll and b/bin/Debug/net10.0-windows/Vmianqian.dll differ diff --git a/bin/Debug/net10.0-windows/Vmianqian.exe b/bin/Debug/net10.0-windows/Vmianqian.exe index 60bd185..35c8bfd 100644 Binary files a/bin/Debug/net10.0-windows/Vmianqian.exe and b/bin/Debug/net10.0-windows/Vmianqian.exe differ diff --git a/bin/Debug/net10.0-windows/Vmianqian.pdb b/bin/Debug/net10.0-windows/Vmianqian.pdb index 2c87c21..d51ee40 100644 Binary files a/bin/Debug/net10.0-windows/Vmianqian.pdb and b/bin/Debug/net10.0-windows/Vmianqian.pdb differ diff --git a/bin/Debug/net10.0-windows/appsettings.client.json b/bin/Debug/net10.0-windows/appsettings.client.json index b06bf84..d0f1fd1 100644 --- a/bin/Debug/net10.0-windows/appsettings.client.json +++ b/bin/Debug/net10.0-windows/appsettings.client.json @@ -1,6 +1,6 @@ { "ServerUrl": "https://zf.yunzer.cn", - "ApiKey": "7eed756ca37ca057370ea9bb208", + "ApiKey": "7eed756ca37ca057370ea9bb208d25fa", "NotifyEmail": "1066960883@qq.com", "WechatPath": "", "WechatSid": "", diff --git a/obj/Debug/net10.0-windows/Vmianqian.AssemblyInfo.cs b/obj/Debug/net10.0-windows/Vmianqian.AssemblyInfo.cs index c3c8df7..d2ca940 100644 --- a/obj/Debug/net10.0-windows/Vmianqian.AssemblyInfo.cs +++ b/obj/Debug/net10.0-windows/Vmianqian.AssemblyInfo.cs @@ -14,7 +14,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("Vmianqian")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1b6455593a6213b2e6fa56d99e52d99b3ceab81b")] [assembly: System.Reflection.AssemblyProductAttribute("Vmianqian")] [assembly: System.Reflection.AssemblyTitleAttribute("Vmianqian")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/obj/Debug/net10.0-windows/Vmianqian.AssemblyInfoInputs.cache b/obj/Debug/net10.0-windows/Vmianqian.AssemblyInfoInputs.cache index 755f1c1..30b50ae 100644 --- a/obj/Debug/net10.0-windows/Vmianqian.AssemblyInfoInputs.cache +++ b/obj/Debug/net10.0-windows/Vmianqian.AssemblyInfoInputs.cache @@ -1 +1 @@ -d7ed290d0d9cfd207fcc9453947b7882a6ba615b446fcf42dcec6201b8c88be8 +947e2bf078e14f00a06d6a610ce423bc783b4ea6779f9a00be7651fb9ae1d425 diff --git a/obj/Debug/net10.0-windows/Vmianqian.dll b/obj/Debug/net10.0-windows/Vmianqian.dll index 9d01bb5..1b359b3 100644 Binary files a/obj/Debug/net10.0-windows/Vmianqian.dll and b/obj/Debug/net10.0-windows/Vmianqian.dll differ diff --git a/obj/Debug/net10.0-windows/Vmianqian.pdb b/obj/Debug/net10.0-windows/Vmianqian.pdb index 2c87c21..d51ee40 100644 Binary files a/obj/Debug/net10.0-windows/Vmianqian.pdb and b/obj/Debug/net10.0-windows/Vmianqian.pdb differ diff --git a/obj/Debug/net10.0-windows/apphost.exe b/obj/Debug/net10.0-windows/apphost.exe index 60bd185..35c8bfd 100644 Binary files a/obj/Debug/net10.0-windows/apphost.exe and b/obj/Debug/net10.0-windows/apphost.exe differ diff --git a/obj/Debug/net10.0-windows/ref/Vmianqian.dll b/obj/Debug/net10.0-windows/ref/Vmianqian.dll index b7eb72b..d60059c 100644 Binary files a/obj/Debug/net10.0-windows/ref/Vmianqian.dll and b/obj/Debug/net10.0-windows/ref/Vmianqian.dll differ diff --git a/obj/Debug/net10.0-windows/refint/Vmianqian.dll b/obj/Debug/net10.0-windows/refint/Vmianqian.dll index b7eb72b..d60059c 100644 Binary files a/obj/Debug/net10.0-windows/refint/Vmianqian.dll and b/obj/Debug/net10.0-windows/refint/Vmianqian.dll differ