Files
KJD/Update/Update/MainForm.cs
T

611 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<ChangelogItem> GetChangeInfos()
{
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")
{
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<string, object>;
if (root["ok"].ToString() == "True")
{
var data = (Dictionary<string, object>)root["data"];
var versionsRaw = (List<object>)data["versions"];
var list = new List<ChangelogItem>();
foreach (Dictionary<string, object> 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.PublishedAt = v.ToString("yyyy-MM-dd");
}
list.Add(ci);
}
return list;
}
}
return null;
}
/// <summary>
/// 渲染更新日志:头部展示版本区间,中间按版本分组列出更新条目
/// </summary>
private void ShowChangelog(string fromVersion, string toVersion, IEnumerable<ChangelogItem> 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;
}));
}
});
}
/// <summary>
/// 拷贝文档目录
/// </summary>
/// <param name="srcPath"></param>
/// <param name="destPath"></param>
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<ChangelogItem> versions { get; set; }
}
/// <summary>
/// 单个版本的更新日志
/// </summary>
public class ChangelogItem
{
public string Version { get; set; }
public string PublishedAt { 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);
}
}
}
/// <summary>
/// 更新日志中的单个版本区块:版本标题行(色条 + 版本号 + 最新标记 + 日期)与条目列表
/// </summary>
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<System.Windows.Forms.Label> lblItems = new List<System.Windows.Forms.Label>();
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<Point> bulletPoints = new List<Point>();
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
}
/// <summary>
/// 更新日志窗口的配色,与设计稿保持一致
/// </summary>
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);
}
}