using COSXML.Network; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Reflection.Emit; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using static Update.ChangelogTheme; namespace Update { public partial class MainForm : Form { public string Title { get { return Text; } set { Text = value; } } private Point mouseOffset; // 记录鼠标指针的坐标 private bool isMouseDown; // 记录鼠标左键是否按下 public MainForm() { InitializeComponent(); this.TopMost = true; lb_title.Text = "更新日志"; var changelogItems = GetChangeInfos(); if (changelogItems != null) { string fromVersion = (Program.oldVersion / 100m).ToString(); string toVersion = (Program.newVersion / 100m).ToString(); ShowChangelog(fromVersion, toVersion, changelogItems); } //// 演示数据:v1.0.0 升级到 v1.3.0,展示 1.1.0 ~ 1.3.0(新的在上) //ShowChangelog("1.0.0", "1.3.0", new[] //{ // new ChangelogItem { Version = "1.3.0", PublishedAt = "2026-10-10", Notes = "新增深色模式,支持跟随系统\n新增快捷键面板(Ctrl + /)\n启动速度提升 40%" }, // new ChangelogItem { Version = "1.2.0", PublishedAt = "2026-09-05", Notes = "支持导出数据为 Excel 格式\n优化列表滚动性能\n修复偶发的保存失败问题" }, // new ChangelogItem { Version = "1.1.0", PublishedAt = "2026-08-01", Notes = "新增自动更新功能\n修复高分屏下界面模糊的问题" }, //}); } private List GetChangeInfos() { try { string paidanUrl = $"https://update-wx-order.e57.cn"; string shanghuoUrl = $"https://update.e57.cn"; using (WebClient c = new WebClient()) { c.Encoding = Encoding.UTF8; string url = $"/api/releases/since?version={(Program.oldVersion / 100m)}&channel=stable"; if (Program.softName == "29WxFindOrder.exe") { url = paidanUrl + url; } else if (Program.softName == "29供应链.exe") url = shanghuoUrl + url; string json = c.DownloadString(url); var root = MiniJson.Json.Deserialize(json) as Dictionary; if (root["ok"].ToString() == "True") { var data = (Dictionary)root["data"]; var versionsRaw = (List)data["versions"]; var list = new List(); foreach (Dictionary item in versionsRaw) { var ci = new ChangelogItem { Version = item["version"]?.ToString(), Notes = item["notes"]?.ToString(), }; if (DateTime.TryParse(item["publishedAt"]?.ToString(), out DateTime v)) { ci.ChangeTime = v; } list.Add(ci); } list.Sort((a, b) => b.ChangeTime.CompareTo(a.ChangeTime)); return list; } } return null; } catch (Exception ex) { return new List { new ChangelogItem { Notes="更新日志获取错误\n"+ex.Message } }; } } /// /// 渲染更新日志:头部展示版本区间,中间按版本分组列出更新条目 /// private void ShowChangelog(string fromVersion, string toVersion, IEnumerable entries) { int length = entries.Count(); lblHeroTitle.Text = "v" + fromVersion + " → v" + toVersion; lblHeroMeta.Text = length > 1 ? "共包含 " + length + " 个版本的更新内容" : "本次更新内容"; panelChangelog.SuspendLayout(); panelChangelog.Controls.Clear(); var blocks = entries.Select((entry, i) => new ChangelogBlock(entry, i == 0, panelChangelog.Width)).ToList(); int contentHeight = blocks.Sum(b => b.Height); // 内容超出可视区时预留垂直滚动条宽度,避免出现横向滚动条 int width = panelChangelog.ClientSize.Width; if (contentHeight > panelChangelog.ClientSize.Height) width -= SystemInformation.VerticalScrollBarWidth; int y = 6; foreach (var block in blocks) { block.Width = width; block.Location = new Point(0, y); y += block.Height; panelChangelog.Controls.Add(block); } panelChangelog.ResumeLayout(true); } private void heroPanel_Paint(object sender, PaintEventArgs e) { var panel = (Panel)sender; if (panel.Width <= 0 || panel.Height <= 0) return; e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; using (var brush = new LinearGradientBrush(panel.ClientRectangle, AccentBg, Color.White, 40F)) e.Graphics.FillRectangle(brush, panel.ClientRectangle); using (var pen = new Pen(LineColor)) { e.Graphics.DrawLine(pen, 0, 0, panel.Width, 0); e.Graphics.DrawLine(pen, 0, panel.Height - 1, panel.Width, panel.Height - 1); } } private void btnOk_Click(object sender, EventArgs e) { if (lblMessage.Text == "升级完成,点击确定重启程序") { string extPath = System.Environment.CurrentDirectory + "\\" + Program.softName.Replace(".exe", "") + ".exe"; System.Diagnostics.Process.Start(extPath); Application.Exit(); return; } btnOk.Enabled = false; CloseMainProgram(); if (!System.IO.Directory.Exists("upBak")) System.IO.Directory.CreateDirectory("upBak"); var upBakPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "upBak"; if (!System.IO.Directory.Exists("upTemp")) System.IO.Directory.CreateDirectory("upTemp"); var upTempPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "upTemp"; // 清空文件夹准备下载 foreach (string filePath in Directory.GetFiles(upTempPath)) { File.Delete(filePath); // 删除文件 } foreach (string dirPath in Directory.GetDirectories(upTempPath)) { Directory.Delete(dirPath, true); // 删除子文件夹及其内容 } this.lblMessage.Text = "升级中请稍候..."; progressBar1.Visible = true; progressBar1.Maximum = 100;//fileNameArr.Count(); progressBar1.Value = 0; Task.Run(async () => { string tempFile = upTempPath + "\\" + Program.updateUrl.Substring(Program.updateUrl.LastIndexOf("/") + 1); //bool result = Program.DownloadFile(Program.updateUrl, tempFile); //while (true) //{ // Invoke(new Action(() => // { // progressBar1.Value = Program.progress; // })); // if (Program.progress == 100) // break; //} ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; using (var downloadClient = new WebClient()) { downloadClient.Proxy = null; downloadClient.DownloadProgressChanged += (s, args) => { Invoke(new Action(() => { progressBar1.Value = args.ProgressPercentage; })); }; await downloadClient.DownloadFileTaskAsync(new Uri(Program.updateUrl), tempFile); } if (1 == 1) //if (result) { ZipFile.ExtractToDirectory(tempFile, upTempPath); File.Delete(tempFile); string mainPath = System.Environment.CurrentDirectory; // 备份旧文件 foreach (string filePath in Directory.GetFiles(upTempPath)) { try { string fileName = filePath.Substring(filePath.LastIndexOf("\\") + 1); File.Copy(mainPath + "\\" + fileName, upBakPath + "\\" + fileName, true); Thread.Sleep(10); } catch { } } foreach (string dirPath in Directory.GetDirectories(upTempPath)) { try { string fileName = dirPath.Substring(dirPath.LastIndexOf("\\") + 1); CopyDirectory(mainPath + "\\" + fileName, upBakPath + "\\" + fileName); Thread.Sleep(10); } catch { } } string error = ""; // 替换旧文件 foreach (string filePath in Directory.GetFiles(upTempPath)) { if (!string.IsNullOrEmpty(error)) { error = "发生错误:\r\n" + error + "\r\n请关闭升级窗口重试!"; break; } try { string fileName = filePath.Substring(filePath.LastIndexOf("\\") + 1); File.Copy(upTempPath + "\\" + fileName, mainPath + "\\" + fileName, true); Thread.Sleep(10); } catch (Exception ex) { error = "发生错误:\r\n" + ex.Message + "\r\n请关闭升级窗口重试!"; Invoke(new Action(() => { lblMessage.Text = error; })); } } foreach (string dirPath in Directory.GetDirectories(upTempPath)) { if (!string.IsNullOrEmpty(error)) break; try { string fileName = dirPath.Substring(dirPath.LastIndexOf("\\") + 1); CopyDirectory(upTempPath + "\\" + fileName, mainPath + "\\" + fileName); Thread.Sleep(10); } catch (Exception ex) { error = "发生错误:\r\n" + ex.Message + "\r\n请关闭升级窗口重试!"; Invoke(new Action(() => { lblMessage.Text = error; })); } } Thread.Sleep(50); Invoke(new Action(() => { if (string.IsNullOrEmpty(error)) { progressBar1.Value = 100; lblMessage.Text = "升级完成,点击确定重启程序"; btnOk.Enabled = true; } })); } else { Invoke(new Action(() => { progressBar1.Value = 100; this.lblMessage.Text = "升级失败,未获取到升级文件"; btnOk.Enabled = true; })); } }); } /// /// 拷贝文档目录 /// /// /// public static void CopyDirectory(string srcPath, string destPath) { try { DirectoryInfo dir = new DirectoryInfo(srcPath); FileSystemInfo[] fileinfo = dir.GetFileSystemInfos(); //获取目录下(不包含子目录)的文件和子目录 foreach (FileSystemInfo i in fileinfo) { if (i is DirectoryInfo) //判断是否文件夹 { if (!Directory.Exists(destPath + "\\" + i.Name)) { Directory.CreateDirectory(destPath + "\\" + i.Name); //目标目录下不存在此文件夹即创建子文件夹 } CopyDirectory(i.FullName, destPath + "\\" + i.Name); //递归调用复制子文件夹 } else { File.Copy(i.FullName, destPath + "\\" + i.Name, true); //不是文件夹即复制文件,true表示可以覆盖同名文件 } } } catch (Exception e) { throw; } } private void btnCancel_Click(object sender, EventArgs e) { this.Close(); } private void panel1_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { mouseOffset = new Point(-e.X, -e.Y); isMouseDown = true; } } private void panel1_MouseUp(object sender, MouseEventArgs e) { isMouseDown = false; } private void panel1_MouseMove(object sender, MouseEventArgs e) { if (isMouseDown) { // 获取鼠标的位置 Point mousePos = MousePosition; mousePos.Offset(mouseOffset.X, mouseOffset.Y); Location = mousePos; } } private void btnClose_Click(object sender, EventArgs e) { Application.Exit(); } private void CloseMainProgram() { string in_sAppName = Program.softName; //(一个word文档程序) in_sAppName = in_sAppName.Replace(".exe", ""); try { System.Diagnostics.Process[] psiNewcas = System.Diagnostics.Process.GetProcessesByName(in_sAppName); //System.Diagnostics.Process.GetProcessesByName(""); //var pro = System.Diagnostics.Process.GetProcesses(); //foreach(var p in pro) //{ // Console.WriteLine(p.ProcessName); //} foreach (System.Diagnostics.Process p in psiNewcas) { p.Kill(); //p.WaitForExit(); //p.Close(); //Console.WriteLine(in_sAppName + "已经停止了"); } } catch { Console.WriteLine(in_sAppName + "停止失败"); } } private void panel2_Paint(object sender, PaintEventArgs e) { } } public class Changelog { public bool Ok { get; set; } public ChangelogData Data { get; set; } } public class ChangelogData { public List versions { get; set; } } /// /// 单个版本的更新日志 /// public class ChangelogItem { public string Version { get; set; } public string PublishedAt => ChangeTime.ToString("yyyy-MM-dd"); public DateTime ChangeTime { get; set; } public string Notes { get; set; } public string[] Items { get { string[] temp = new string[0]; if (string.IsNullOrEmpty(Notes)) return temp; return Notes.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); } } } /// /// 更新日志中的单个版本区块:版本标题行(色条 + 版本号 + 最新标记 + 日期)与条目列表 /// public class ChangelogBlock : Panel { private const int LeftPad = 28; private const int RightPad = 28; private const int BarWidth = 3; private const int BarHeight = 16; private const int TextLeft = 55; // 条目文字左边,留出圆点位置 private const int ItemPadding = 5; // 条目上下内边距 private const int BottomGap = 20; // 区块底部留白 private const int HeaderHeight = 28; private readonly bool isNewest; private readonly Font versionFont = new Font("微软雅黑", 12F, FontStyle.Bold); private readonly Font dateFont = new Font("微软雅黑", 9F); private readonly Font badgeFont = new Font("微软雅黑", 8.5F); private readonly Font itemFont = new Font("微软雅黑", 10.5F); private readonly Size versionSize; private readonly Size dateSize; private readonly List lblItems = new List(); private readonly System.Windows.Forms.Label lblVersion = new System.Windows.Forms.Label(); private readonly System.Windows.Forms.Label lblDate = new System.Windows.Forms.Label(); private Rectangle badgeRect = Rectangle.Empty; private Rectangle barRect = Rectangle.Empty; private readonly List bulletPoints = new List(); private int laidOutWidth = -1; public ChangelogBlock(ChangelogItem entry, bool isNewest, int width) { this.isNewest = isNewest; BackColor = Color.White; DoubleBuffered = true; versionSize = TextRenderer.MeasureText("v" + entry.Version, versionFont); dateSize = TextRenderer.MeasureText(entry.PublishedAt, dateFont); lblVersion.Font = versionFont; lblVersion.ForeColor = Accent; lblVersion.BackColor = Color.White; lblVersion.Text = "v" + entry.Version; Controls.Add(lblVersion); lblDate.Font = dateFont; lblDate.ForeColor = SubColor; lblDate.BackColor = Color.White; lblDate.Text = entry.PublishedAt; Controls.Add(lblDate); foreach (string item in entry.Items) { var label = new System.Windows.Forms.Label { Font = itemFont, ForeColor = TextColor, BackColor = Color.White, Padding = new Padding(0, ItemPadding, 0, ItemPadding), Text = item }; lblItems.Add(label); Controls.Add(label); } Width = width; } protected override void OnResize(EventArgs eventargs) { base.OnResize(eventargs); LayoutContent(Width); } private void LayoutContent(int width) { if (width <= 0 || width == laidOutWidth) return; laidOutWidth = width; SuspendLayout(); barRect = new Rectangle(LeftPad, (HeaderHeight - BarHeight) / 2, BarWidth, BarHeight); lblVersion.SetBounds(LeftPad + BarWidth + 8, (HeaderHeight - versionSize.Height) / 2, versionSize.Width, versionSize.Height); badgeRect = Rectangle.Empty; int headerRight = lblVersion.Right; if (isNewest) { Size badgeSize = TextRenderer.MeasureText("最新", badgeFont, Size.Empty, TextFormatFlags.NoPadding); badgeRect = new Rectangle(headerRight + 8, (HeaderHeight - 18) / 2, badgeSize.Width + 16, 18); headerRight = badgeRect.Right; } lblDate.SetBounds(Math.Max(headerRight + 8, width - RightPad - dateSize.Width), (HeaderHeight - dateSize.Height) / 2, dateSize.Width, dateSize.Height); int lineHeight = TextRenderer.MeasureText("中", itemFont, Size.Empty, TextFormatFlags.NoPadding).Height; int textWidth = Math.Max(60, width - TextLeft - RightPad); bulletPoints.Clear(); int y = HeaderHeight; foreach (var label in lblItems) { int measured = TextRenderer.MeasureText(label.Text, itemFont, new Size(textWidth, 0), TextFormatFlags.WordBreak).Height; label.SetBounds(TextLeft, y, textWidth, measured + ItemPadding * 2); bulletPoints.Add(new Point(TextLeft - 14, y + ItemPadding + lineHeight / 2)); y += label.Height; } Height = y + BottomGap; ResumeLayout(false); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics g = e.Graphics; g.SmoothingMode = SmoothingMode.AntiAlias; using (var path = RoundedRect(barRect, BarWidth / 2f)) using (var brush = new SolidBrush(Accent)) g.FillPath(brush, path); if (isNewest && badgeRect.Width > 0) { using (var path = RoundedRect(badgeRect, badgeRect.Height / 2f)) using (var brush = new SolidBrush(Accent)) g.FillPath(brush, path); TextRenderer.DrawText(g, "最新", badgeFont, badgeRect, Color.White, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPadding); } using (var dot = new SolidBrush(Accent)) foreach (Point p in bulletPoints) g.FillEllipse(dot, p.X - 3, p.Y - 3, 6, 6); } private static GraphicsPath RoundedRect(Rectangle rect, float radius) { var path = new GraphicsPath(); float d = radius * 2; path.AddArc(rect.X, rect.Y, d, d, 180, 90); path.AddArc(rect.Right - d, rect.Y, d, d, 270, 90); path.AddArc(rect.Right - d, rect.Bottom - d, d, d, 0, 90); path.AddArc(rect.X, rect.Bottom - d, d, d, 90, 90); path.CloseFigure(); return path; } } public enum FormLevel { SMALL, MEDIUM, LARGE = 500 } /// /// 更新日志窗口的配色,与设计稿保持一致 /// internal static class ChangelogTheme { public static readonly Color Accent = Color.FromArgb(152, 99, 143); public static readonly Color AccentBg = Color.FromArgb(245, 237, 244); public static readonly Color SubColor = Color.FromArgb(107, 114, 128); public static readonly Color LineColor = Color.FromArgb(229, 231, 238); public static readonly Color TextColor = Color.FromArgb(31, 36, 48); } }