Chr
2024-08-20 45c004d4bb5a6f73843a8e8020523f4df14a14e4
完善消息通知程序;
8个文件已添加
7个文件已修改
1344 ■■■■■ 已修改文件
PdmAlert/.vs/PdmAlert/v17/DocumentLayout.json 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/App.xaml.cs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/DockApp.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/Encryptor.cs 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/Entity/MsgData.cs 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/Icon/iconfont.ttf 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/LoginUser.cs 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/LoginWindow.xaml 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/LoginWindow.xaml.cs 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/MainWindow.xaml 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/MainWindow.xaml.cs 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/PdmAlert.csproj 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/Util.cs 452 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/app.manifest 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/lib/Newtonsoft.Json.dll 补丁 | 查看 | 原始文档 | blame | 历史
PdmAlert/.vs/PdmAlert/v17/DocumentLayout.json
New file
@@ -0,0 +1,184 @@
{
  "Version": 1,
  "WorkspaceRootPath": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\",
  "Documents": [
    {
      "AbsoluteMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|c:\\main\\workspace\\visualstudio\\pdmswplugin2\\pdmalert\\loginuser.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|solutionrelative:loginuser.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\loginwindow.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|solutionrelative:loginwindow.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\mainwindow.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|solutionrelative:mainwindow.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\app.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|solutionrelative:app.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|c:\\main\\workspace\\visualstudio\\pdmswplugin2\\pdmalert\\loginwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}",
      "RelativeMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|solutionrelative:loginwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\util.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|solutionrelative:util.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}",
      "RelativeMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|solutionrelative:mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\entity\\msgdata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|solutionrelative:entity\\msgdata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\dockapp.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|solutionrelative:dockapp.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\encryptor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{FC7FBDE7-D0D2-4E79-A586-501ABC73FE46}|PdmAlert.csproj|solutionrelative:encryptor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    }
  ],
  "DocumentGroupContainers": [
    {
      "Orientation": 0,
      "VerticalTabListWidth": 256,
      "DocumentGroups": [
        {
          "DockedWidth": 200,
          "SelectedChildIndex": 7,
          "Children": [
            {
              "$type": "Document",
              "DocumentIndex": 4,
              "Title": "LoginWindow.xaml",
              "DocumentMoniker": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\LoginWindow.xaml",
              "RelativeDocumentMoniker": "LoginWindow.xaml",
              "ToolTip": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\LoginWindow.xaml",
              "RelativeToolTip": "LoginWindow.xaml",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|",
              "WhenOpened": "2024-08-20T10:32:06.895Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 5,
              "Title": "Util.cs",
              "DocumentMoniker": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\Util.cs",
              "RelativeDocumentMoniker": "Util.cs",
              "ToolTip": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\Util.cs",
              "RelativeToolTip": "Util.cs",
              "ViewState": "AQIAAKgBAAAAAAAAAAAowLMBAAARAAAA",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2024-08-20T10:17:23.879Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 7,
              "Title": "MsgData.cs",
              "DocumentMoniker": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\Entity\\MsgData.cs",
              "RelativeDocumentMoniker": "Entity\\MsgData.cs",
              "ToolTip": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\Entity\\MsgData.cs",
              "RelativeToolTip": "Entity\\MsgData.cs",
              "ViewState": "AQIAAAAAAAAAAAAAAAAAAAgAAAAAAAAA",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2024-08-20T09:14:07.015Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 6,
              "Title": "MainWindow.xaml",
              "DocumentMoniker": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\MainWindow.xaml",
              "RelativeDocumentMoniker": "MainWindow.xaml",
              "ToolTip": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\MainWindow.xaml",
              "RelativeToolTip": "MainWindow.xaml",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|",
              "WhenOpened": "2024-08-20T09:08:54.87Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 2,
              "Title": "MainWindow.xaml.cs",
              "DocumentMoniker": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\MainWindow.xaml.cs",
              "RelativeDocumentMoniker": "MainWindow.xaml.cs",
              "ToolTip": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\MainWindow.xaml.cs",
              "RelativeToolTip": "MainWindow.xaml.cs",
              "ViewState": "AQIAAGIAAAAAAAAAAAAnwJEAAAAsAAAA",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2024-08-20T09:03:17.694Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 8,
              "Title": "DockApp.cs",
              "DocumentMoniker": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\DockApp.cs",
              "RelativeDocumentMoniker": "DockApp.cs",
              "ToolTip": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\DockApp.cs",
              "RelativeToolTip": "DockApp.cs",
              "ViewState": "AQIAABkAAAAAAAAAAAAkwA4AAAArAAAA",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2024-08-20T06:58:33.822Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 9,
              "Title": "Encryptor.cs",
              "DocumentMoniker": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\Encryptor.cs",
              "RelativeDocumentMoniker": "Encryptor.cs",
              "ToolTip": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\Encryptor.cs",
              "RelativeToolTip": "Encryptor.cs",
              "ViewState": "AQIAACIAAAAAAAAAAAAawBIAAAAuAAAA",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2024-08-20T06:23:54.361Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 0,
              "Title": "LoginUser.cs",
              "DocumentMoniker": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\LoginUser.cs",
              "RelativeDocumentMoniker": "LoginUser.cs",
              "ToolTip": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\LoginUser.cs",
              "RelativeToolTip": "LoginUser.cs",
              "ViewState": "AQIAAEYAAAAAAAAAAAAywFgAAAANAAAA",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2024-08-20T03:50:43.52Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 1,
              "Title": "LoginWindow.xaml.cs",
              "DocumentMoniker": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\LoginWindow.xaml.cs",
              "RelativeDocumentMoniker": "LoginWindow.xaml.cs",
              "ToolTip": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\LoginWindow.xaml.cs",
              "RelativeToolTip": "LoginWindow.xaml.cs",
              "ViewState": "AQIAAAYAAAAAAAAAAAAAABAAAAAfAAAA",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2024-08-20T03:46:25.163Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 3,
              "Title": "App.xaml.cs",
              "DocumentMoniker": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\App.xaml.cs",
              "RelativeDocumentMoniker": "App.xaml.cs",
              "ToolTip": "C:\\Main\\Workspace\\VisualStudio\\PdmSwPlugin2\\PdmAlert\\App.xaml.cs",
              "RelativeToolTip": "App.xaml.cs",
              "ViewState": "AQIAAAgAAAAAAAAAAAAIwBgAAAAjAAAA",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2024-08-20T03:42:24.667Z",
              "EditorCaption": ""
            }
          ]
        }
      ]
    }
  ]
}
PdmAlert/App.xaml.cs
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows;
namespace PdmAlert
{
@@ -14,16 +8,27 @@
    public partial class App : Application
    {
        DockApp dockApp;
        LoginWindow loginWindow;
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            this.ShutdownMode = ShutdownMode.OnExplicitShutdown;
            dockApp.Run();
            if (LoginUser.TryAutoLogin())
            {
                dockApp = new DockApp();
                dockApp.Run();
            }
            else
            {
                loginWindow = new LoginWindow();
                loginWindow.Show();
            }
        }
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            dockApp = new DockApp();
        }
    }
}
PdmAlert/DockApp.cs
@@ -38,6 +38,7 @@
            if (showWindow != null)
            {
                showWindow.Show();
                showWindow.Activate();
            }
            else
            {
PdmAlert/Encryptor.cs
New file
@@ -0,0 +1,63 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace PdmAlert
{
    public class Encryptor
    {
        private readonly byte[] key;
        private readonly byte[] iv;
        public Encryptor(string keyString, string ivString)
        {
            key = Encoding.UTF8.GetBytes(keyString);
            iv = Encoding.UTF8.GetBytes(ivString);
        }
        public string Encrypt(string plainText)
        {
            using (var aes = Aes.Create())
            {
                aes.Key = key;
                aes.IV = iv;
                ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(plainText);
                        }
                        return Convert.ToBase64String(msEncrypt.ToArray());
                    }
                }
            }
        }
        public string Decrypt(string cipherText)
        {
            using (var aes = Aes.Create())
            {
                aes.Key = key;
                aes.IV = iv;
                ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
                byte[] arr = Convert.FromBase64String(cipherText);
                using (MemoryStream msDecrypt = new MemoryStream(arr))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {
                            return srDecrypt.ReadToEnd();
                        }
                    }
                }
            }
        }
    }
}
PdmAlert/Entity/MsgData.cs
New file
@@ -0,0 +1,8 @@
namespace PdmAlert.Entity
{
    public class MsgData
    {
        public string title { get; set; }
        public string content { get; set; }
    }
}
PdmAlert/Icon/iconfont.ttf
Binary files differ
PdmAlert/LoginUser.cs
New file
@@ -0,0 +1,117 @@
using PdmAlert.Util;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Reflection;
namespace PdmAlert
{
    public class LoginUser
    {
        public static LoginUser CurrentUser { get; private set; }
        private static readonly Encryptor encryptor = new Encryptor("12345678123456781234567812345678",
            "1234567812345678");
        public string id { get; set; }
        public string realName { get; set; }
        public string username { get; set; }
        public string password { get; set; }
        public string appId { get; set; }
        public string pluginVersion { get; set; }
        public bool rememberMe { get; set; }
        public static bool TryAutoLogin()
        {
            string fileContent = null;
            if (LoadStrFromBin(ref fileContent))
            {
                try
                {
                    string jsonStr = encryptor.Decrypt(fileContent);
                    Dictionary<string, string> user = JsonUtil.Deserialize<Dictionary<string, string>>(jsonStr);
                    return Login(user["username"], user["password"], true);
                }
                catch (Exception ex)
                {
                    RemoveCacheBin();
                    return false;
                }
            }
            return false;
        }
        public static bool Login(string username, string password, bool rememberMe)
        {
            using (HttpClient client = new HttpClient
            {
                BaseAddress = new Uri("http://localhost:8888/pdm-web/"),
            })
            {
                try
                {
                    Result<LoginUser> res = client.PostSyncAction<LoginUser>(new LoginUser
                    {
                        username = username,
                        password = password,
                        appId = "sockettool",
                        pluginVersion = "0.0.0.1"
                    }, "openApi/wpf/login");
                    CurrentUser = res.HandleResult();
                    CurrentUser.password = password;
                    CurrentUser.rememberMe = rememberMe;
                    if (rememberMe)
                    {
                        string userInfo = JsonUtil.Stringfiy(CurrentUser);
                        SaveStrToBin(encryptor.Encrypt(userInfo));
                    }
                    return true;
                }
                catch (Exception ex)
                {
                    RemoveCacheBin();
                }
            }
            return false;
        }
        public static void SaveStrToBin(string content)
        {
            string filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().FullName), "bin");
            byte[] bytes = Convert.FromBase64String(content);
            using (Stream stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
            using (BinaryWriter writer = new BinaryWriter(stream))
            {
                writer.Write(bytes);
            }
        }
        public static bool LoadStrFromBin(ref string content)
        {
            string filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().FullName), "bin");
            if (!File.Exists(filePath))
            {
                return false;
            }
            using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            using (BinaryReader reader = new BinaryReader(stream))
            {
                byte[] bytes = new byte[stream.Length];
                reader.Read(bytes, 0, bytes.Length);
                content = Convert.ToBase64String(bytes);
                return true;
            }
        }
        public static void RemoveCacheBin()
        {
            string filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().FullName), "bin");
            File.Delete(filePath);
        }
    }
}
PdmAlert/LoginWindow.xaml
@@ -6,7 +6,27 @@
        xmlns:local="clr-namespace:PdmAlert"
        mc:Ignorable="d"
        Title="LoginWindow" Height="450" Width="800">
    <Grid>
    </Grid>
    <AdornerDecorator>
        <Border x:Name="mainBorder">
            <Grid Height="200" VerticalAlignment="Top" Margin="20">
                <Grid.RowDefinitions>
                    <RowDefinition Height="30"/>
                    <RowDefinition Height="30"/>
                    <RowDefinition Height="30"/>
                    <RowDefinition Height="50"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="50"/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Label Content="用户名" VerticalContentAlignment="Center" Grid.Row="0" Grid.Column="0"/>
                <TextBox x:Name="usernameTextBlock" VerticalAlignment="Stretch" VerticalContentAlignment="Center" Grid.Row="0" Grid.Column="1"/>
                <Label Content="密码" VerticalContentAlignment="Center" Grid.Row="1" Grid.Column="0"/>
                <PasswordBox x:Name="passwordTextBlock" VerticalAlignment="Stretch" VerticalContentAlignment="Center" Grid.Row="1" Grid.Column="1"/>
                <CheckBox Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" x:Name="rememberMeInput">自动登录</CheckBox>
                <Button Margin="0,10,0,0"  Click="Button_Click" Padding="5" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2">登录</Button>
            </Grid>
        </Border>
    </AdornerDecorator>
</Window>
PdmAlert/LoginWindow.xaml.cs
@@ -1,16 +1,4 @@
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;
using System.Windows;
namespace PdmAlert
{
@@ -23,5 +11,15 @@
        {
            InitializeComponent();
        }
        public void Button_Click(object sender, RoutedEventArgs e)
        {
            if (LoginUser.Login(usernameTextBlock.Text, passwordTextBlock.Password, rememberMeInput.IsChecked == true))
            {
            }
        }
    }
}
PdmAlert/MainWindow.xaml
@@ -6,115 +6,135 @@
        xmlns:local="clr-namespace:PdmAlert"
        mc:Ignorable="d"
        WindowStyle="None"
        Topmost="True"
        ResizeMode="NoResize"
        WindowStartupLocation="Manual"
        Loaded="Window_Loaded"
        Deactivated="Window_Deactivated"
        ShowInTaskbar="False"
        Title="MainWindow" Height="600" Width="400">
    <Window.Resources>
        <Brush x:Key="MainBorder">#0d4d84</Brush>
        <Brush x:Key="MainMenu">#1d6eb2</Brush>
        <Brush x:Key="ChildBorder">#3692e1</Brush>
        <Brush x:Key="MainStatusBar">#1d6eb2</Brush>
        <ResourceDictionary>
            <FontFamily x:Key="iconfont">
                /PdmAlert;component/Icon/#iconfont
            </FontFamily>
            <Brush x:Key="MainBorder">#0d4d84</Brush>
            <Brush x:Key="MainMenu">#1d6eb2</Brush>
            <Brush x:Key="ChildBorder">#3692e1</Brush>
            <Brush x:Key="MainStatusBar">#1d6eb2</Brush>
        <Brush x:Key="SecondaryImportant">Orange</Brush>
        <Brush x:Key="Important">#e17933</Brush>
        <Brush x:Key="ImportantFont">#FFF</Brush>
            <Brush x:Key="SecondaryImportant">Orange</Brush>
            <Brush x:Key="Important">#e17933</Brush>
            <Brush x:Key="ImportantFont">#FFF</Brush>
        <Brush x:Key="ButtonColor">#103156</Brush>
        <Brush x:Key="ButtonSelected">#02579E</Brush>
        <Brush x:Key="ButtonFont">White</Brush>
        <Brush x:Key="ButtonFontSelected">White</Brush>
        <Style x:Key="WindowButton" TargetType="Button">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="FontWeight" Value="SemiBold"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Width" Value="50"/>
            <Setter Property="FontSize" Value="20"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="BorderBrush" Value="Transparent"/>
            <Brush x:Key="ButtonColor">#103156</Brush>
            <Brush x:Key="ButtonSelected">#02579E</Brush>
            <Brush x:Key="ButtonFont">White</Brush>
            <Brush x:Key="ButtonFontSelected">White</Brush>
            <Style x:Key="WindowButton" TargetType="Button">
                <Setter Property="FontFamily" Value="{StaticResource iconfont}"/>
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="FontWeight" Value="SemiBold"/>
                <Setter Property="Foreground" Value="Black"/>
                <Setter Property="Width" Value="50"/>
                <Setter Property="FontSize" Value="20"/>
                <Setter Property="BorderThickness" Value="1"/>
                <Setter Property="BorderBrush" Value="Transparent"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border x:Name="border" Width="{TemplateBinding Width}" Background="{TemplateBinding Background}"
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Button">
                            <Border x:Name="border" Width="{TemplateBinding Width}" Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0">
                            <Label x:Name="label" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                                <Label x:Name="label" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                           HorizontalContentAlignment="Center" VerticalContentAlignment="Center" 
                           Foreground="{TemplateBinding Foreground}"
                                   Content="{TemplateBinding Content}"/>
                        </Border>
                            </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="border" Property="Background" Value="Gray"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter TargetName="border" Property="Background" Value="Gray"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        <Style x:Key="WindowCloseButton" BasedOn="{StaticResource WindowButton}" TargetType="Button">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border x:Name="border" Width="{TemplateBinding Width}" Background="{TemplateBinding Background}"
            <Style x:Key="WindowCloseButton" BasedOn="{StaticResource WindowButton}" TargetType="Button">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Button">
                            <Border x:Name="border" Width="{TemplateBinding Width}" Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0">
                            <Label x:Name="label" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                                <Label x:Name="label" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
               HorizontalContentAlignment="Center" VerticalContentAlignment="Center" 
               Foreground="{TemplateBinding Foreground}"
                       Content="{TemplateBinding Content}"/>
                        </Border>
                            </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="border" Property="Background" Value="Red"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter TargetName="border" Property="Background" Value="Red"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        <Style x:Key="ChildWindowButton" BasedOn="{StaticResource WindowButton}" TargetType="Button">
            <Setter Property="FontSize" Value="15"/>
            <Setter Property="Width" Value="30"/>
            <Setter Property="Background" Value="{StaticResource ChildBorder}"/>
        </Style>
            <Style x:Key="ChildWindowButton" BasedOn="{StaticResource WindowButton}" TargetType="Button">
                <Setter Property="FontSize" Value="15"/>
                <Setter Property="Width" Value="30"/>
                <Setter Property="Background" Value="{StaticResource ChildBorder}"/>
            </Style>
        <Style x:Key="ChildWindowCloseButton" BasedOn="{StaticResource ChildWindowButton}" TargetType="Button">
        </Style>
            <Style x:Key="ChildWindowCloseButton" BasedOn="{StaticResource ChildWindowButton}" TargetType="Button">
        <Style x:Key="LinkButton" TargetType="Button">
            <Setter Property="FontFamily" Value="{StaticResource iconfont}"/>
            <Setter Property="Foreground" Value="{StaticResource ChildBorder}"/>
            </Style>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <TextBlock Style="{x:Null}" x:Name="label" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
            <Style x:Key="LinkButton" TargetType="Button">
                <Setter Property="FontFamily" Value="{StaticResource iconfont}"/>
                <Setter Property="Foreground" Value="{StaticResource ChildBorder}"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Button">
                            <TextBlock Style="{x:Null}" x:Name="label" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                           Foreground="{TemplateBinding Foreground}"
                                   Text="{TemplateBinding Content}"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ResourceDictionary>
    </Window.Resources>
    <StackPanel>
        <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
            <Button Style="{StaticResource WindowButton}" Click="Button_Click">刷新</Button>
            <Button Style="{StaticResource WindowButton}" Click="Button_Click">切换用户</Button>
            <Button Style="{StaticResource WindowCloseButton}" Click="Hide_Click">最小化</Button>
        </StackPanel>
        <Grid>
            <Button Click="Button_Click">测试</Button>
        </Grid>
        <Border BorderThickness="0" Background="LightGray">
            <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
                <Button Style="{StaticResource WindowButton}" Click="Refresh_Click" BorderBrush="Black" ToolTip="刷新">&#xe600;</Button>
                <Button Style="{StaticResource WindowButton}" Click="SwitchUser_Click" ToolTip="切换用户">&#xe6ed;</Button>
                <Button Style="{StaticResource WindowCloseButton}" Click="Hide_Click" ToolTip="最小化">&#xe67a;</Button>
            </StackPanel>
        </Border>
        <Label Content="{Binding messageTitle}"/>
        <ListView ItemsSource="{Binding messages}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Label Content="999"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackPanel>
</Window>
PdmAlert/MainWindow.xaml.cs
@@ -2,27 +2,80 @@
using DevComponents.DotNetBar;
using System.Windows;
using System.Drawing;
using System.Net.WebSockets;
using System;
using System.Threading.Tasks;
using System.Text;
using System.Diagnostics;
using System.Timers;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using PdmAlert.Entity;
namespace PdmAlert
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        public virtual event PropertyChangedEventHandler PropertyChanged;
        protected virtual void RaisePropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        protected void RaiseAndSetIfChanged<T>(ref T old, T @new, [CallerMemberName] string propertyName = null)
        {
            old = @new;
            if (propertyName != null)
            {
                RaisePropertyChanged(propertyName);
            }
        }
        #endregion
        private ClientWebSocket socket;
        private Task readTask;
        private Task writeTask;
        private System.Timers.Timer heartbeatTimer;
        private string _messageTitle = "共有 0 条未读消息";
        public string messageTitle
        {
            get => _messageTitle;
            set => RaiseAndSetIfChanged(ref _messageTitle, value);
        }
        private ObservableCollection<MsgData> _messages;
        public ObservableCollection<MsgData> messages
        {
            get => _messages;
            set => RaiseAndSetIfChanged(ref _messages, value);
        }
        public MainWindow()
        {
            InitializeComponent();
        }
        private long _RunningAlertId = 0;
        private void Button_Click(object sender, RoutedEventArgs e)
        private void Refresh_Click(object sender, RoutedEventArgs e)
        {
            eDesktopAlertColor color = eDesktopAlertColor.Default;
            eAlertPosition position = eAlertPosition.BottomRight;
            DesktopAlert.Show("123", "\uf005", eSymbolSet.Awesome, Color.Empty, color, position, 10, ++_RunningAlertId, AlertClicked);
        }
        private void SwitchUser_Click(object sender, RoutedEventArgs e)
        {
        }
        private void AlertClicked(long alertId)
        {
@@ -35,6 +88,108 @@
            this.Left = screeWidth - this.Width;
            this.Top = sHeight - this.Height;
            ConnectWebSocket();
        }
        private async Task ConnectWebSocket()
        {
            try
            {
                ClientWebSocket ws = new ClientWebSocket();
                await ws.ConnectAsync(new Uri($"ws://localhost:8888/pdm-web/daws/{LoginUser.CurrentUser?.id}"), default);
                socket = ws;
                ReadMsg();
                StartHeartbeat();
            }
            catch (Exception ex)
            {
                await ConnectWebSocket();
            }
        }
        private void ReadMsg()
        {
            readTask = Task.Run(() =>
            {
                while (socket.State == WebSocketState.Open)
                {
                    byte[] buffer = new byte[1024];
                    var task = socket.ReceiveAsync(new ArraySegment<byte>(buffer), default);
                    task.Wait();
                    var res = task.Result;
                    var msg = Encoding.UTF8.GetString(buffer, 0, res.Count);
                    HandleMsg(msg);
                }
            });
        }
        private void HandleMsg(string msg)
        {
            if (string.IsNullOrEmpty(msg) || msg == "heartcheck")
            {
                return;
            }
            Debug.WriteLine(msg);
            Dispatcher.Invoke(() =>
            {
                eDesktopAlertColor color = eDesktopAlertColor.Default;
                eAlertPosition position = eAlertPosition.BottomRight;
                DesktopAlert.Show(msg, "\uf005", eSymbolSet.Awesome, Color.Empty, color, position, 5,
                    DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), AlertClicked);
            });
        }
        private void StartHeartbeat()
        {
            heartbeatTimer = new System.Timers.Timer(30 * 1000);
            heartbeatTimer.Elapsed += HeartbeatTimer_Elapsed;
            heartbeatTimer.AutoReset = true;
            heartbeatTimer.Start();
        }
        private void HeartbeatTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (socket == null || socket.State != WebSocketState.Open)
            {
                DoDispose();
                ConnectWebSocket().Wait();
            }
            else
            {
                byte[] buffer = Encoding.UTF8.GetBytes("test");
                socket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, default);
            }
        }
        public void DoDispose()
        {
            if (heartbeatTimer != null)
            {
                heartbeatTimer.Stop();
                heartbeatTimer.Dispose();
                heartbeatTimer = null;
            }
            if (readTask != null)
            {
                readTask.Dispose();
                readTask = null;
            }
            if (socket != null)
            {
                try
                {
                    socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "DoDispose", default).Wait();
                    socket.Dispose();
                }
                catch (Exception ex)
                {
                }
                finally
                {
                    socket = null;
                }
            }
        }
        private void Hide_Click(object sender, RoutedEventArgs e)
@@ -42,5 +197,9 @@
            this.Hide();
        }
        private void Window_Deactivated(object sender, EventArgs e)
        {
            this.Hide();
        }
    }
}
PdmAlert/PdmAlert.csproj
@@ -54,13 +54,20 @@
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <PropertyGroup>
    <ApplicationManifest>app.manifest</ApplicationManifest>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="DevComponents.DotNetBar2">
      <HintPath>..\lib\DevComponents.DotNetBar2.dll</HintPath>
    </Reference>
    <Reference Include="Newtonsoft.Json">
      <HintPath>lib\Newtonsoft.Json.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Web" />
    <Reference Include="System.Windows.Forms" />
    <Reference Include="System.Xml" />
    <Reference Include="Microsoft.CSharp" />
@@ -80,6 +87,7 @@
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </ApplicationDefinition>
    <Compile Include="Util.cs" />
    <Page Include="LoginWindow.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
@@ -93,6 +101,9 @@
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="DockApp.cs" />
    <Compile Include="Encryptor.cs" />
    <Compile Include="Entity\MsgData.cs" />
    <Compile Include="LoginUser.cs" />
    <Compile Include="LoginWindow.xaml.cs">
      <DependentUpon>LoginWindow.xaml</DependentUpon>
    </Compile>
@@ -119,6 +130,8 @@
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
    <None Include="app.manifest" />
    <Resource Include="Icon\iconfont.ttf" />
    <None Include="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
@@ -127,7 +140,6 @@
  <ItemGroup>
    <None Include="App.config" />
  </ItemGroup>
  <ItemGroup />
  <ItemGroup>
    <Content Include="Icon\Icon.ico">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
PdmAlert/Util.cs
New file
@@ -0,0 +1,452 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json;
namespace PdmAlert.Util
{
    [Serializable]
    public class Result<T>
    {
        [JsonProperty("success")]
        public bool success
        {
            get; set;
        }
        [JsonProperty("message")]
        public string message
        {
            get; set;
        }
        [JsonProperty("code")]
        public int code
        {
            get; set;
        }
        [JsonProperty("result")]
        public T result
        {
            get; set;
        }
    }
    public static class ResultHandler
    {
        public static T HandleResult<T>(this Result<T> result)
        {
            if (result.success)
            {
                return result.result;
            }
            throw new CustomWebException(result.message);
        }
    }
    /// <summary>
    /// 自定义从Web返回的异常
    /// </summary>
    public class CustomWebException : Exception
    {
        public CustomWebException()
        {
        }
        public CustomWebException(string message) : base(message)
        {
        }
        public CustomWebException(string message, Exception inner) : base(message, inner)
        {
        }
    }
    public class JsonUtil
    {
        static readonly JsonSerializerSettings JsSetting = new JsonSerializerSettings()
        {
            NullValueHandling = NullValueHandling.Ignore
        };
        public static T Deserialize<T>(string jsonStr)
        {
            return JsonConvert.DeserializeObject<T>(jsonStr);
        }
        public static string Serialize(object obj)
        {
            return JsonConvert.SerializeObject(obj);
        }
        public static string Stringfiy(object obj)
        {
            return JsonConvert.SerializeObject(obj, Formatting.Indented, JsSetting);
        }
    }
    public static class HttpUtil
    {
        /// <summary>
        /// Http GET 同步获取对象
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="client">客户端</param>
        /// <param name="url">url</param>
        /// <param name="models">参数</param>
        /// <returns>Http返回结果</returns>
        public static Result<T> GetSyncAction<T>(this HttpClient client, string url, params object[] models)
        {
            return JsonUtil.Deserialize<Result<T>>(GetSyncAction(client, url, models));
        }
        /// <summary>
        /// Http GET 同步获取字符
        /// </summary>
        /// <param name="client">客户端</param>
        /// <param name="url">url</param>
        /// <param name="models">参数</param>
        /// <returns>Http返回结果</returns>
        public static string GetSyncAction(this HttpClient client, string url, params object[] models)
        {
            try
            {
                foreach (object model in models)
                {
                    url = url.ModelToUriParam(model);
                }
                Task<string> task = client.GetStringAsync(url);
                task.Wait();
                return task.Result;
            }
            catch (Exception e)
            {
                throw new CustomWebException("请求异常", e);
            }
        }
        /// <summary>
        /// Http GET 异步获取对象
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="client">客户端</param>
        /// <param name="url">url</param>
        /// <param name="models">参数</param>
        /// <returns>异步Http返回结果</returns>
        public static async Task<Result<T>> GetAsyncAction<T>(this HttpClient client, string url, params object[] models)
        {
            try
            {
                foreach (object model in models)
                {
                    url = url.ModelToUriParam(model);
                }
                return JsonUtil.Deserialize<Result<T>>(await client.GetStringAsync(url));
            }
            catch (Exception e)
            {
                throw new CustomWebException("请求异常", e);
            }
        }
        /// <summary>
        /// Http POST 同步获取对象
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="client">客户端</param>
        /// <param name="url">url</param>
        /// <param name="data">参数json字符</param>
        /// <returns>Http返回结果</returns>
        public static Result<T> PostSyncAction<T>(this HttpClient client, string url, string data)
        {
            StringContent content = new StringContent(data, Encoding.UTF8, "application/json");
            Task<HttpResponseMessage> task = client.PostAsync(url, content);
            task.Wait();
            HttpResponseMessage response = task.Result;
            response.EnsureSuccessStatusCode();
            Task<string> task2 = response.Content.ReadAsStringAsync();
            task2.Wait();
            string responseBody = task2.Result;
            return JsonUtil.Deserialize<Result<T>>(responseBody);
        }
        /// <summary>
        /// Http POST 同步获取对象
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="client">客户端</param>
        /// <param name="url">url</param>
        /// <param name="content">Http负载</param>
        /// <returns>Http返回结果</returns>
        public static Result<T> PostSyncAction<T>(this HttpClient client, string url, HttpContent content)
        {
            Task<HttpResponseMessage> task = client.PostAsync(url, content);
            task.Wait();
            HttpResponseMessage response = task.Result;
            response.EnsureSuccessStatusCode();
            Task<string> task2 = response.Content.ReadAsStringAsync();
            task2.Wait();
            return JsonUtil.Deserialize<Result<T>>(task2.Result);
        }
        /// <summary>
        /// Http POST 同步获取对象
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="client">客户端</param>
        /// <param name="data">参数对象</param>
        /// <param name="url">url</param>
        /// <returns>Http返回结果</returns>
        public static Result<T> PostSyncAction<T>(this HttpClient client, object data, string url)
        {
            string dataStr = JsonUtil.Serialize(data);
            StringContent content = new StringContent(dataStr, Encoding.UTF8, "application/json");
            Task<HttpResponseMessage> task = client.PostAsync(url, content);
            task.Wait();
            HttpResponseMessage response = task.Result;
            response.EnsureSuccessStatusCode();
            Task<string> task2 = response.Content.ReadAsStringAsync();
            task2.Wait();
            string responseBody = task2.Result;
            return JsonUtil.Deserialize<Result<T>>(responseBody);
        }
        public static string PostSyncAction(this HttpClient client, string url, string data)
        {
            StringContent content = new StringContent(data, Encoding.UTF8, "application/json");
            Task<HttpResponseMessage> task = client.PostAsync(url, content);
            task.Wait();
            HttpResponseMessage response = task.Result;
            response.EnsureSuccessStatusCode();
            Task<string> task2 = response.Content.ReadAsStringAsync();
            task2.Wait();
            return task2.Result;
        }
        public static async Task<T> PostAsyncAction<T>(this HttpClient client, string url, string data)
        {
            StringContent content = new StringContent(data, Encoding.UTF8, "application/json");
            HttpResponseMessage response = await client.PostAsync(url, content);
            _ = response.EnsureSuccessStatusCode();
            return JsonUtil.Deserialize<T>(await response.Content.ReadAsStringAsync());
        }
        public static async Task<Result<T>> PostAsyncAction<T>(this HttpClient client, string url, object data)
        {
            string dataStr = JsonUtil.Serialize(data);
            StringContent content = new StringContent(dataStr, Encoding.UTF8, "application/json");
            HttpResponseMessage response = await client.PostAsync(url, content);
            _ = response.EnsureSuccessStatusCode();
            return JsonUtil.Deserialize<Result<T>>(await response.Content.ReadAsStringAsync());
        }
        public static async Task<Result<T>> PostAsyncAction<T>(this HttpClient client, string url, HttpContent content)
        {
            HttpResponseMessage response = await client.PostAsync(url, content);
            _ = response.EnsureSuccessStatusCode();
            return JsonUtil.Deserialize<Result<T>>(await response.Content.ReadAsStringAsync());
        }
        /// <summary>
        /// 下载文件保存到绝对路径
        /// </summary>
        /// <param name="client"></param>
        /// <param name="url"></param>
        /// <param name="absolutePath"></param>
        /// <param name="models"></param>
        public static async Task GetDownload(this HttpClient client, string url, string absolutePath, params object[] models)
        {
            foreach (object model in models)
            {
                url = url.ModelToUriParam(model);
            }
            HttpResponseMessage response = await client.GetAsync(url);
            int code = Convert.ToInt32(response.StatusCode);
            if (code == 200)
            {
                using (Stream stream = await response.Content.ReadAsStreamAsync())
                {
                    string suffix = Path.GetExtension(response.RequestMessage.RequestUri.ToString());
                    using (FileStream fs = new FileStream(absolutePath, FileMode.OpenOrCreate))
                    {
                        byte[] buffer = new byte[1024];
                        int readLength = 0;
                        int length;
                        while ((length = await stream.ReadAsync(buffer, 0, buffer.Length)) != 0)
                        {
                            readLength += length;
                            fs.Write(buffer, 0, length);
                        }
                    }
                }
            }
            else
            {
                string error = "图纸下载失败";
                if (response.Headers.TryGetValues("FileName", out IEnumerable<string> values))
                {
                    error = values.First();
                }
                throw new CustomWebException(error);
            }
        }
        /// <summary>
        /// 下载文件保存到绝对路径
        /// </summary>
        /// <param name="client"></param>
        /// <param name="url"></param>
        /// <param name="absolutePath"></param>
        /// <param name="models"></param>
        public static void GetDownloadSync(this HttpClient client, string url, string absolutePath, params object[] models)
        {
            foreach (object model in models)
            {
                url = url.ModelToUriParam(model);
            }
            Task<HttpResponseMessage> task = client.GetAsync(url);
            task.Wait();
            HttpResponseMessage response = task.Result;
            int code = Convert.ToInt32(response.StatusCode);
            if (code == 200)
            {
                Task<Stream> task2 = response.Content.ReadAsStreamAsync();
                task2.Wait();
                using (Stream stream = task2.Result)
                {
                    string suffix = Path.GetExtension(response.RequestMessage.RequestUri.ToString());
                    using (FileStream fs = new FileStream(absolutePath, FileMode.OpenOrCreate))
                    {
                        byte[] buffer = new byte[1024];
                        int readLength = 0;
                        int length;
                        while ((length = stream.Read(buffer, 0, buffer.Length)) != 0)
                        {
                            readLength += length;
                            fs.Write(buffer, 0, length);
                        }
                    }
                }
            }
            else
            {
                string error = "图纸下载失败";
                if (response.Headers.TryGetValues("FileName", out IEnumerable<string> values))
                {
                    error = HttpUtility.UrlDecode(values.First());
                }
                throw new CustomWebException(error);
            }
        }
        /// <summary>
        /// 下载文件到指定文件夹
        /// </summary>
        /// <param name="client"></param>
        /// <param name="url"></param>
        /// <param name="dir"></param>
        /// <param name="fileName"></param>
        /// <param name="models"></param>
        public static async Task GetDownload(this HttpClient client, string url, string dir, string fileName, params object[] models)
        {
            foreach (object model in models)
            {
                url = url.ModelToUriParam(model);
            }
            HttpResponseMessage response = await client.GetAsync(url);
            using (Stream stream = await response.Content.ReadAsStreamAsync())
            {
                string suffix = Path.GetExtension(response.RequestMessage.RequestUri.ToString());
                using (FileStream fs = new FileStream($"{dir}/{fileName}", FileMode.CreateNew))
                {
                    byte[] buffer = new byte[1024];
                    int readLength = 0;
                    int length;
                    while ((length = await stream.ReadAsync(buffer, 0, buffer.Length)) != 0)
                    {
                        readLength += length;
                        fs.Write(buffer, 0, length);
                    }
                }
            }
        }
        public static async void GetDownloadWithName(this HttpClient client, string url, string dir, params object[] models)
        {
            foreach (object model in models)
            {
                url = url.ModelToUriParam(model);
            }
            HttpResponseMessage response = await client.GetAsync(url);
            string fileName = HttpUtility.UrlDecode(response.Headers.GetValues("FileName").FirstOrDefault());
            using (Stream stream = await response.Content.ReadAsStreamAsync())
            {
                string suffix = Path.GetExtension(response.RequestMessage.RequestUri.ToString());
                using (FileStream fs = new FileStream($"{dir}/{fileName}", FileMode.CreateNew))
                {
                    byte[] buffer = new byte[1024];
                    int readLength = 0;
                    int length;
                    while ((length = await stream.ReadAsync(buffer, 0, buffer.Length)) != 0)
                    {
                        readLength += length;
                        fs.Write(buffer, 0, length);
                    }
                }
            }
        }
        public static string ModelToUriParam(this string url, object obj)
        {
            PropertyInfo[] properties = obj.GetType().GetProperties();
            StringBuilder sb = new StringBuilder()
                .Append(url).Append("?");
            if (obj is Dictionary<string, string>)
            {
                Dictionary<string, string> dict = (Dictionary<string, string>)obj;
                foreach (var item in dict)
                {
                    if (item.Value == null || string.IsNullOrWhiteSpace(item.Value))
                    {
                        continue;
                    }
                    sb = sb.Append(item.Key)
                       .Append("=")
                       .Append(HttpUtility.UrlEncode(item.Value))
                       .Append("&");
                }
            }
            else
            {
                foreach (PropertyInfo p in properties)
                {
                    object v = p.GetValue(obj, null);
                    if (v == null || string.IsNullOrWhiteSpace(v.ToString()))
                    {
                        continue;
                    }
                    sb = sb.Append(p.Name)
                        .Append("=")
                        .Append(HttpUtility.UrlEncode(v.ToString()))
                        .Append("&");
                }
            }
            sb = sb.Remove(sb.Length - 1, 1);
            return sb.ToString();
        }
    }
}
PdmAlert/app.manifest
New file
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
    <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
        <security>
            <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
                <!-- UAC 清单选项
             如果想要更改 Windows 用户帐户控制级别,请使用
             以下节点之一替换 requestedExecutionLevel 节点。
        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />
            指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
            如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
            元素。
        -->
                <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
            </requestedPrivileges>
        </security>
    </trustInfo>
    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
        <application>
            <!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
           Windows 版本的列表。取消评论适当的元素,
           Windows 将自动选择最兼容的环境。 -->
            <!-- Windows Vista -->
            <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
            <!-- Windows 7 -->
            <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
            <!-- Windows 8 -->
            <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
            <!-- Windows 8.1 -->
            <!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
            <!-- Windows 10 -->
            <!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
        </application>
    </compatibility>
    <!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
       自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI,无需
       选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
       在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
       将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
    <!--
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
      <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
    </windowsSettings>
  </application>
  -->
    <!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
    <!--
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.Common-Controls"
          version="6.0.0.0"
          processorArchitecture="*"
          publicKeyToken="6595b64144ccf1df"
          language="*"
        />
    </dependentAssembly>
  </dependency>
  -->
</assembly>
PdmAlert/lib/Newtonsoft.Json.dll
Binary files differ