From 1846636e4575555b596e56141589b34cfc8c5797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E6=BD=87=E9=98=B3?= Date: Fri, 17 May 2024 14:51:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AksWebBrowser.sln | 37 + .../AKS.EnterpriseLibrary.WebBrowser.csproj | 51 + CPF_Cef/AssistEntity.cs | 16 + CPF_Cef/Common/COMUtils.cs | 321 +++++ CPF_Cef/Common/ChunkedUpload.cs | 58 + CPF_Cef/Common/Log.cs | 82 ++ CPF_Cef/Common/Utils.cs | 21 + CPF_Cef/CusWebBrowser.cs | 77 ++ CPF_Cef/FrmMain.cs | 202 +++ CPF_Cef/MainModel.cs | 1206 +++++++++++++++++ CPF_Cef/Parame.cs | 25 + CPF_Cef/Program.cs | 38 + CPF_Cef/Recent.ico | Bin 0 -> 83695 bytes CPF_Cef/SetTaskStatus.cs | 51 + CPF_Cef/StyleSheet.css | 473 +++++++ 15 files changed, 2658 insertions(+) create mode 100644 AksWebBrowser.sln create mode 100644 CPF_Cef/AKS.EnterpriseLibrary.WebBrowser.csproj create mode 100644 CPF_Cef/AssistEntity.cs create mode 100644 CPF_Cef/Common/COMUtils.cs create mode 100644 CPF_Cef/Common/ChunkedUpload.cs create mode 100644 CPF_Cef/Common/Log.cs create mode 100644 CPF_Cef/Common/Utils.cs create mode 100644 CPF_Cef/CusWebBrowser.cs create mode 100644 CPF_Cef/FrmMain.cs create mode 100644 CPF_Cef/MainModel.cs create mode 100644 CPF_Cef/Parame.cs create mode 100644 CPF_Cef/Program.cs create mode 100644 CPF_Cef/Recent.ico create mode 100644 CPF_Cef/SetTaskStatus.cs create mode 100644 CPF_Cef/StyleSheet.css diff --git a/AksWebBrowser.sln b/AksWebBrowser.sln new file mode 100644 index 0000000..2d69e9e --- /dev/null +++ b/AksWebBrowser.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34622.214 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AKS.EnterpriseLibrary.WebBrowser", "CPF_Cef\AKS.EnterpriseLibrary.WebBrowser.csproj", "{76142658-6E83-4A1C-8CC9-C2574865CA7D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Debug|x64.ActiveCfg = Debug|x64 + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Debug|x64.Build.0 = Debug|x64 + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Debug|x86.ActiveCfg = Debug|x86 + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Debug|x86.Build.0 = Debug|x86 + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Release|Any CPU.Build.0 = Release|Any CPU + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Release|x64.ActiveCfg = Release|x64 + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Release|x64.Build.0 = Release|x64 + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Release|x86.ActiveCfg = Release|x86 + {76142658-6E83-4A1C-8CC9-C2574865CA7D}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BDDA2FFD-1C83-470F-82EE-DE0E448EEB6D} + EndGlobalSection +EndGlobal diff --git a/CPF_Cef/AKS.EnterpriseLibrary.WebBrowser.csproj b/CPF_Cef/AKS.EnterpriseLibrary.WebBrowser.csproj new file mode 100644 index 0000000..9e4a5de --- /dev/null +++ b/CPF_Cef/AKS.EnterpriseLibrary.WebBrowser.csproj @@ -0,0 +1,51 @@ + + + + WinExe + net6.0 + Recent.ico + + AksWebBrowser + AksWebBrowser + AnyCPU;x64;x86 + + + + true + + AnyCPU + + + + true + + AnyCPU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CPF_Cef/AssistEntity.cs b/CPF_Cef/AssistEntity.cs new file mode 100644 index 0000000..bc200c8 --- /dev/null +++ b/CPF_Cef/AssistEntity.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AksWebBrowser +{ + public enum LightTypeModel + { + FingerLight, + IDCardLight, + BottomLight, + BurnLight + } +} diff --git a/CPF_Cef/Common/COMUtils.cs b/CPF_Cef/Common/COMUtils.cs new file mode 100644 index 0000000..708e29a --- /dev/null +++ b/CPF_Cef/Common/COMUtils.cs @@ -0,0 +1,321 @@ +using AKS.EnterpriseLibrary.WebBrowser; +using AksWebBrowser; +using AKSWebBrowser.Commen; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.IO.Ports; +using System.Text; +using System.Threading.Tasks; +using System.Timers; +using Timer = System.Timers.Timer; +namespace AKSWebBrowser.Common +{ + public class COMUtils + { + private static SerialPort serialPort = new SerialPort(); + public string jsonstr = string.Empty; + public string jsontemp = string.Empty; + public int maxCHunkSize = 1024; + public string callback = string.Empty; + public string ml = "COM4"; + public COMUtils() + { + OpenCOM(); + } + + //打开COM口 + public void OpenCOM() + { + + try + { + //要执行的Linux命令 + string[] cmd = LinuxCmdArea("ls /dev"); + if (cmd.Length > 0) + { + string parm = string.Empty; + foreach (string line in cmd) + { + if (line.Contains("ttyCH341USB")) + { + parm = line; + break; + } + } + if (!string.IsNullOrEmpty(parm)) + { + ml = "/dev/" + parm; + Log.Info("输出结果:" + ml); + //给管理权限 + LinuxCmd(ml); + //打开串口 + // 设置COM口,波特率,奇偶校验,数据位,停止位 + serialPort.PortName = ml; // 请替换为你的串口名称 + serialPort.BaudRate = 115200; // 设置波特率 + serialPort.Parity = Parity.None; + serialPort.DataBits = 8; + serialPort.StopBits = StopBits.One; + serialPort.Handshake = Handshake.None; + serialPort.DtrEnable = true; //启用控制终端就续信号 + //serialPort.ReadTimeout = 18000; + //serialPort.RtsEnable = true; //启用请求发送信号 + serialPort.NewLine = "\n"; + serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler); + if (!serialPort.IsOpen) + { + serialPort.Open(); + } + Timer timer = new Timer(3000);//1秒钟的时间间隔 + timer.Elapsed += OnTimedEvent; + timer.AutoReset = true;//重复执行 + timer.Enabled = true;//启动定时器 + Log.Info("浏览器COM服务启动成功"); + } + else + { + Log.Info("串口类型不匹配"); + } + } + else + { + Log.Info("当前设备没有串口设备"); + } + } + catch (Exception ex) + { + Log.Info("服务启动异常ex: " + ex.Message + ""); + } + + } + + //接受数据 + public static string bsid = string.Empty; + public static string bsext = string.Empty; + public static string bspath = string.Empty; + public static bool sfjswc = false; + private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) + { + try + { + SerialPort sp = (SerialPort)sender; + string _jsonstr = sp.ReadExisting(); + if (!string.IsNullOrEmpty(_jsonstr)) + { + if (_jsonstr.Contains("\n")) + { + jsonstr = jsontemp + _jsonstr; + //向js发送数据 + //CShaseBJavaScript(jsonstr); + //jsontemp = string.Empty; + //jsonstr = string.Empty; + } + else + { + jsontemp = jsontemp + _jsonstr; + } + } + } + catch (Exception ex) + { + Log.Info("接受数据数据异常: " + ex.Message + ""); + jsonstr = MainModel.str2Base64("{\"callback\":\"" + this.callback + "\",\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "接受数据数据超时:" + ex.Message + "\"}"); + // CShaseBJavaScript(jsonstr); + } + } + + //打开串口 + private void OnTimedEvent(Object source, ElapsedEventArgs e) + { + try + { + if (!serialPort.IsOpen) + { + serialPort.Open(); + } + } + catch (Exception ex) + { + Log.Info("定时任务打开串口异常: " + ex.Message + ""); + } + } + + //发送数据 + public string SendData(string data, string callback) + { + try + { + this.callback = callback; + if (serialPort.IsOpen) + { + jsontemp = string.Empty; + jsonstr = string.Empty; + //写入数据并以换行符结束 + serialPort.WriteLine(data); + Log.Info("发送数据成功: " + data + ""); + while (string.IsNullOrEmpty(jsonstr)) + { + Task.Delay(10).Wait(); + } + } + else + { + //重新打开串口 + OpenCOM(); + if (serialPort.IsOpen) + { + jsontemp = string.Empty; + jsonstr = string.Empty; + //写入数据并以换行符结束 + serialPort.WriteLine(data); + Log.Info("发送数据成功: " + data + ""); + while (string.IsNullOrEmpty(jsonstr)) + { + Task.Delay(10).Wait(); + } + } + else + { + jsontemp = string.Empty; + jsonstr = string.Empty; + Log.Info("发送数据失败"); + jsonstr = MainModel.str2Base64("{\"callback\":\"" + this.callback + "\",\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "串口未打开" + "\"}"); + //CShaseBJavaScript(jsonstr); + } + } + } + catch (Exception ex) + { + jsontemp = string.Empty; + jsonstr = string.Empty; + Log.Info("发送数据异常3: " + ex.Message + ""); + jsonstr = MainModel.str2Base64("{\"callback\":\"" + this.callback + "\",\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "发送数据异常:" + ex.Message + "\"}"); + //CShaseBJavaScript(jsonstr); + } + return jsonstr; + } + + //关闭 + public void ClosePort() + { + try + { + if (serialPort.IsOpen) + { + serialPort.Close(); // 关闭串口 + } + } + catch (Exception ex) + { + Log.Error("关闭异常: " + ex.Message + ""); + } + } + + /// + /// COM接收文件 + /// + public void NewMethod1(SerialPort sp, string path) + { + byte[] buffer = new byte[sp.ReadBufferSize]; + int bytesRead = sp.Read(buffer, 0, buffer.Length); + using (FileStream fileStream = new FileStream(path, FileMode.Append)) + { + fileStream.Write(buffer, 0, bytesRead); + fileStream.Close(); + fileStream.Dispose(); + } + } + + + /// + /// COM口公共发送文件 + /// + /// + public void NewMethod(string url, string id) + { + string ext = Path.GetExtension(url); + serialPort.WriteLine("Start_" + id + "_" + ext); + // 发送端 + byte[] documentBytes = File.ReadAllBytes(url); + serialPort.Write(documentBytes, 0, documentBytes.Length); + serialPort.WriteLine("End"); + } + + + //向js传输数据 + public void CShaseBJavaScript(string param) + { + Task.Run(async () => + { + param = MainModel.Base64str2(param); + Log.Info("返回数据:" + param); + JObject jo = (JObject)JsonConvert.DeserializeObject(param); + bool fieldExists = jo.ContainsKey("callback"); + if (fieldExists) + { + string callback = jo["callback"].ToString(); + Log.Info("回调js方法:" + callback); + string _parm = callback + "('" + param + "')"; + await Parame.webBrowser.ExecuteJavaScript(_parm); + } + else + { + Log.Info("回调js方法为空"); + } + }); + } + + //执行命令 + public void LinuxCmd(string command) + { + command = $"echo 'aks@123456' sudo -S chmod 777 {command}"; + Log.Info("执行命令:" + command); + // 启动进程 + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "/bin/bash", + Arguments = $"-c \"{command}\"", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + } + }; + process.Start(); + string output = process.StandardOutput.ReadToEnd(); + string error = process.StandardError.ReadToEnd(); + process.WaitForExit(); + Log.Info("执行命令结果返回:" + output); + Log.Info("执行命令错误结果返回:" + error); + } + + //执行命令返回数组 + public string[] LinuxCmdArea(string command) + { + // 使用ProcessStartInfo设置启动参数 + ProcessStartInfo startInfo = new ProcessStartInfo + { + FileName = "/bin/bash", // 指定bash shell + Arguments = $"-c \"{command}\"", // 要执行的命令 + RedirectStandardOutput = true, // 重定向标准输出 + UseShellExecute = false, // 不使用系统外壳程序启动 + CreateNoWindow = true // 不创建新窗口 + }; + + // 启动进程 + using (Process process = Process.Start(startInfo)) + { + using (System.IO.StreamReader reader = process.StandardOutput) + { + string result = reader.ReadToEnd(); // 读取全部输出 + return result.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); // 转换为字符串数组 + } + } + } + } +} diff --git a/CPF_Cef/Common/ChunkedUpload.cs b/CPF_Cef/Common/ChunkedUpload.cs new file mode 100644 index 0000000..f6f9759 --- /dev/null +++ b/CPF_Cef/Common/ChunkedUpload.cs @@ -0,0 +1,58 @@ +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using CPF.Controls; + +namespace AksWebBrowser.Common +{ + public class ChunkedUpload + { + private readonly HttpClient _httpClient; + + public ChunkedUpload(HttpClient httpClient) + { + _httpClient = httpClient; + } + + public async Task UploadFileAsync(string url, string filePath) + { + string ret = string.Empty; + using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + FileInfo fileInfo = new FileInfo(filePath); + int totalParts = 1; + int chunkNumber = 1; + // 读取文件流其实位置 + var fileStreamPos = 0; + var uploadUrl = $"{url}?partNumber={chunkNumber}&chunks={totalParts}&size={fileInfo.Length}&start={fileStreamPos}&end={fileInfo.Length}&total={fileInfo.Length}&FileName={Path.GetFileName(filePath)}"; + + using (var client = new HttpClient()) + { + + var formData = new MultipartFormDataContent(); + formData.Add(new StreamContent(fileStream, (int)fileStream.Length), "file", Path.GetFileName(filePath) + ".partNumber-1"); + var response = await client.PostAsync(uploadUrl, formData); + var responseString = await response.Content.ReadAsStringAsync(); + fileStream.Close(); + fileStream.Dispose(); + ret = responseString; + JObject jo = (JObject)JsonConvert.DeserializeObject(ret); + if (Convert.ToBoolean(jo["IsSucceed"].ToString()) == true) + { + string result = jo["result"].ToString(); + JObject jo1 = (JObject)JsonConvert.DeserializeObject(result); + ret = jo1["url"].ToString(); + } + } + } + return ret; + } + } +} diff --git a/CPF_Cef/Common/Log.cs b/CPF_Cef/Common/Log.cs new file mode 100644 index 0000000..adc6668 --- /dev/null +++ b/CPF_Cef/Common/Log.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AKSWebBrowser.Commen +{ + public class Log + { + private static StreamWriter streamWriter; //写文件 + + public static void Error(string message) + { + try + { + //DateTime dt = new DateTime(); + string directPath = AppDomain.CurrentDomain.BaseDirectory + @"/logs/error"; //在获得文件夹路径 + if (!Directory.Exists(directPath)) //判断文件夹是否存在,如果不存在则创建 + { + Directory.CreateDirectory(directPath); + } + directPath += string.Format(@"/{0}.log", DateTime.Now.ToString("yyyy-MM-dd")); + if (streamWriter == null) + { + streamWriter = !File.Exists(directPath) ? File.CreateText(directPath) : File.AppendText(directPath); + } + streamWriter.WriteLine("***********************************************************************"); + streamWriter.WriteLine(DateTime.Now.ToString("HH:mm:ss")); + streamWriter.WriteLine("输出信息:错误信息"); + if (message != null) + { + streamWriter.WriteLine("异常信息:\r\n" + message); + } + } + finally + { + if (streamWriter != null) + { + streamWriter.Flush(); + streamWriter.Dispose(); + streamWriter = null; + } + } + } + + public static void Info(string message) + { + try + { + //DateTime dt = new DateTime(); + string directPath = AppDomain.CurrentDomain.BaseDirectory + @"/logs/Info"; //在获得文件夹路径 + if (!Directory.Exists(directPath)) //判断文件夹是否存在,如果不存在则创建 + { + Directory.CreateDirectory(directPath); + } + directPath += string.Format(@"/{0}.log", DateTime.Now.ToString("yyyy-MM-dd")); + if (streamWriter == null) + { + streamWriter = !File.Exists(directPath) ? File.CreateText(directPath) : File.AppendText(directPath); + } + streamWriter.WriteLine("***********************************************************************"); + streamWriter.WriteLine(DateTime.Now.ToString("HH:mm:ss")); + streamWriter.WriteLine("输出信息:信息"); + if (message != null) + { + streamWriter.WriteLine("信息:\r\n" + message); + } + } + finally + { + if (streamWriter != null) + { + streamWriter.Flush(); + streamWriter.Dispose(); + streamWriter = null; + } + } + } + } +} diff --git a/CPF_Cef/Common/Utils.cs b/CPF_Cef/Common/Utils.cs new file mode 100644 index 0000000..5616b15 --- /dev/null +++ b/CPF_Cef/Common/Utils.cs @@ -0,0 +1,21 @@ +using CPF.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AksWebBrowser.Common +{ + public class Utils + { + /// + /// 消息弹框 + /// + /// + public static void MessagesBox(string mes) { + MessageBox.ShowSync(mes); + + } + } +} diff --git a/CPF_Cef/CusWebBrowser.cs b/CPF_Cef/CusWebBrowser.cs new file mode 100644 index 0000000..d51b8b7 --- /dev/null +++ b/CPF_Cef/CusWebBrowser.cs @@ -0,0 +1,77 @@ +using CPF; +using CPF.Cef; +using CPF.Cef.JSExtenstions; +using CPF.Mac.CoreText; +using CPF.Reflection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.ConstrainedExecution; +using System.Text; +using System.Threading.Tasks; + +namespace AKS.EnterpriseLibrary.WebBrowser +{ + + public class CusWebBrowser : CPF.Cef.WebBrowser + { + public CusCefRequestHandler CusRequest = new CusCefRequestHandler(); + public CusWebBrowser() { } + + protected override CpfCefClient OnCreateWebBrowser(CefBrowserSettings settings) + { + CpfCefClient cefClient = base.OnCreateWebBrowser(settings); + cefClient.RequestHandler = CusRequest; + cefClient.ContextMenuHandler = new MenuHandler(); + cefClient.DragHandler = new DragHandler(); + return cefClient; + } + } + + public class CusCefRequestHandler : CpfCefRequestHandler + { + public delegate void CusResquestDelegate(CefPostData postData, CefRequest request); + public event CusResquestDelegate CusResquestEvent; + + public CusCefRequestHandler() { } + + protected override CefResourceRequestHandler GetResourceRequestHandler(CefBrowser browser, CefFrame frame, CefRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling) + { + CusResquestEvent(request.PostData, request); + return null; + } + } + + public class DragHandler : CpfCefDragHandler + { + protected override bool OnDragEnter(CefBrowser browser, CefDragData dragData, CefDragOperationsMask mask) + { + return true; + } + protected override void OnDraggableRegionsChanged(CefBrowser browser, CefFrame frame, CefDraggableRegion[] regions) + { + + } + } + + public class MenuHandler : CpfCefContextMenuHandler + { + protected override void OnBeforeContextMenu(CefBrowser browser, CefFrame frame, CefContextMenuParams state, CefMenuModel model) + { + model.Clear(); + } + protected override bool OnContextMenuCommand(CefBrowser browser, CefFrame frame, CefContextMenuParams state, int commandId, CefEventFlags eventFlags) + { + return false; + } + protected override void OnContextMenuDismissed(CefBrowser browser, CefFrame frame) + { + + } + protected override bool RunContextMenu(CefBrowser browser, CefFrame frame, CefContextMenuParams parameters, CefMenuModel model, CefRunContextMenuCallback callback) + { + return false; + } + } + +} diff --git a/CPF_Cef/FrmMain.cs b/CPF_Cef/FrmMain.cs new file mode 100644 index 0000000..cae73a7 --- /dev/null +++ b/CPF_Cef/FrmMain.cs @@ -0,0 +1,202 @@ +using AksWebBrowser; +using CPF; +using CPF.Cef; +using CPF.Controls; +using CPF.Platform; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using AKSWebBrowser.Commen; +using System.Collections.Generic; +using System.Reflection.Metadata; +using AksWebBrowser.Common; + +namespace AKS.EnterpriseLibrary.WebBrowser +{ + public class FrmMain : Window + { + + protected override void InitializeComponent() + { + LoadStyleFile("res://AksWebBrowser/StyleSheet.css"); + //加载样式文件,文件需要设置为内嵌资源 + Title = "控申业务专用浏览器"; + CanResize = false; + ShowInTaskbar = true; + WindowState = WindowState.Maximized; + Children.Add( + new Border + { + Width = "100%", + Height = "100%", + Child = new Panel + { + Size = SizeField.Fill, + Children = + { + new CusWebBrowser + { + PresenterFor = this, + Name = nameof(Parame.webBrowser), + Bindings = + { + { + nameof(CusWebBrowser.Title), + "Title", + this, + BindingMode.OneWayToSource + }, + }, + MarginTop=0, + MarginLeft=0, + MarginRight=0, + MarginBottom=0, + }, + } + } + } + ); + } + private TextBox textBox; + protected override async void OnInitialized() + { + //窗体大小 + this.Width = 1080; + this.Height = 1920; + //SetTaskStatus.Hidetask(); + base.OnInitialized(); + Parame.webBrowser = FindPresenterByName(nameof(Parame.webBrowser)); + textBox = FindPresenterByName(nameof(textBox)); + Parame.webBrowser.CusRequest.CusResquestEvent += CusRequest_CusResquestEvent; + //浏览器大小 + Parame.webBrowser.Width = 1080; + Parame.webBrowser.Height = 1920; + Parame.webBrowser.Url = "http://192.168.0.34:8078/#/main-out"; + //Parame.webBrowser.Url = Application.StartupPath + @"\html\index.html"; + //开发者工具暂时只能支持Windows + //webBrowser.ShowDev(); + //SetTaskStatus.Showtask(); + Parame.webBrowser.LoadEnd += WebBrowser_LoadEnd; + this.Closing += MainWindow_Closing; + } + + //关闭事件 + private void MainWindow_Closing(object sender, ClosingEventArgs e) + { + new MainModel().CLoseCOM(); + MainModel.KillProcessByName("AksWebBrowser"); + } + bool showDev = false; + private void WebBrowser_LoadEnd(object sender, LoadEndEventArgs e) + { + if (!showDev) + { + showDev = true; + Parame.webBrowser.ShowDev(); + } + //获取授权 + GetSQObject(); + } + + public void Writelog(string str, string dirName = @"logs") + { + try + { + if (string.IsNullOrEmpty(str)) return; + + var baseDir = AppDomain.CurrentDomain.BaseDirectory + dirName; + if (!Directory.Exists(baseDir)) + { + Directory.CreateDirectory(baseDir); + } + string filePath = System.IO.Path.Combine(baseDir, DateTime.Now.ToString("yyyy-MM-dd") + "_log.txt"); + using (StreamWriter sw = File.AppendText(filePath)) + { + sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "[" + str + "]" + "\r\n"); + sw.Flush(); + } + } + catch { } + } + private void CusRequest_CusResquestEvent(CefPostData postData, CefRequest request) + { + string postParams = string.Empty; + if (postData != null) + { + //取提交的参数 + CefPostData cefPostData = postData; + var elements = cefPostData.GetElements(); + foreach (var element in elements) + { + if (element.ElementType == CefPostDataElementType.Bytes) + { + var bytes = element.GetBytes(); + postParams = Encoding.UTF8.GetString(bytes); + Console.WriteLine("请求参数:" + postParams); + } + } + } + + Console.WriteLine("请求地址:" + request.Url.ToString()); + } + + //调用JS内的JS方法 + async void InvokeJS(CpfObject obj, RoutedEventArgs eventArgs) + { + var r = await Parame.webBrowser.ExecuteJavaScript("callback('调用绑定到JS里的C#方法')"); + } + + /// + /// 或者可以用授权接口 + /// + public void GetSQObject() + { + Task.Run(async () => + { + var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Get, $"{Parame.apiUrl}/api/Interface/Getlist?Ytjbm={Parame.key}"); + var response = await client.SendAsync(request); + List list = new List(); + if (response.StatusCode.ToString() == "200") + { + response.EnsureSuccessStatusCode(); + var body = await response.Content.ReadAsStringAsync(); + JObject jo = (JObject)JsonConvert.DeserializeObject(body); + if (jo["IsSucceed"].ToString() == "True") + { + string result = jo["result"].ToString(); + if (!string.IsNullOrEmpty("result")) + { + JArray array = (JArray)JsonConvert.DeserializeObject(result); + foreach (JObject item in array) + { + Func func = new Func() + { + Id = item["Id"].ToString(), + Platform = item["Platform"].ToString(), + Interfaceaddress = item["Interfaceaddress"].ToString(), + }; + list.Add(func); + } + } + else + { + Log.Info("未获取授权"); + Utils.MessagesBox("未获取授权"); + } + } + else + { + Log.Info("未获取授权"); + Utils.MessagesBox("未获取授权"); + } + } + Parame.FuncObject = list; + }); + } + } +} diff --git a/CPF_Cef/MainModel.cs b/CPF_Cef/MainModel.cs new file mode 100644 index 0000000..0dcd33e --- /dev/null +++ b/CPF_Cef/MainModel.cs @@ -0,0 +1,1206 @@ +using AksWebBrowser; +using AksWebBrowser.Common; +using AKSWebBrowser.Commen; +using AKSWebBrowser.Common; +using CPF.Cef; +using CPF.Mac.AppKit; +using NAudio.Wave; +using NAudio.Wave.SampleProviders; +using SkiaSharp; +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using System.Xml.Linq; + +namespace AKS.EnterpriseLibrary.WebBrowser +{ + public class MainModel : CPF.CpfObject + { + public COMUtils com = new COMUtils(); + public string callback = string.Empty; + public string PrinterName = "Lexmark-MS430-Series"; + public static Process recordingProcess; + public static Process Typrocess; + + /// + /// 读取身份证卡号 + /// + /// + /// + [JSFunction] + public string IDCardRead(string paramsString) + { + try + { + if (!isFuncisFuncObject("IDCardRead")) + { + Utils.MessagesBox("读取身份证设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "读取身份证设备未授权使用" + "\"}"; + } + else + { + paramsString = "{\"callback\":\"" + callback + "\",\"type\":\"1\",\"param\":{\"data\":\"" + "" + "\"}}"; + Log.Info("读取身份证卡号: " + paramsString + ""); + string base64 = str2Base64(paramsString); + string str = com.SendData(base64, callback); + string result = Base64str2(str); + SubmitLogs(result, "IDCardRead"); + return result; + } + } + catch (Exception ex) + { + Log.Error("读取身份证卡号异常: " + ex.Message + ""); + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "IDCardRead"); + return result; + } + } + + /// + /// 打印排队票据 + /// + /// 排号 + /// 等待人数 + /// 二维码 + /// 办理业务名称 + /// + [JSFunction] + public string SendByPrint(string ph, string ddrs, string qrcode, string ywmc) + { + try + { + if (!isFuncisFuncObject("SendByPrint")) + { + Utils.MessagesBox("打印排队票据设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "打印排队票据设备未授权使用" + "\"}"; + } + else + { + string paramsString = "{\"callback\":\"" + callback + "\",\"type\":\"2\",\"param\":{\"ph\":\"" + ph + "\",\"ddrs\":\"" + ddrs + "\",\"qrcode\":\"" + qrcode + "\",\"ywmc\":\"" + ywmc + "\"}}"; + Log.Info("打印排队票据: " + paramsString + ""); + string base64 = str2Base64(paramsString); + string str = com.SendData(base64, callback); + string result = Base64str2(str); + SubmitLogs(result, "SendByPrint"); + return result; + } + } + catch (Exception ex) + { + Log.Error("打印排队票据异常: " + ex.Message + ""); + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "SendByPrint"); + return result; + } + } + + /// + /// 文字语音播报 + /// apt install sox + /// apt install libsox-fmt-all + /// + /// + /// + /// + public WaveOutEvent playerTxt = null; + public static string tempWav = string.Empty; + [JSFunction] + public string payleText(string text, bool ispaye) + { + try + { + if (!isFuncisFuncObject("payleText")) + { + Utils.MessagesBox("文字语音播报设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "文字语音播报设备未授权使用" + "\"}"; + } + else + { + string paramsString = "{\"callback\":\"" + callback + "\",\"type\":\"3\",\"param\":{\"text\":\"" + text + "\",\"ispaye\":\"" + ispaye + "\"}}"; + Log.Info("文字语音播报: " + paramsString + ""); + //string base64 = str2Base64(paramsString); + //if (base64.Length > 1024) + //{ + // //形成临时文件 + // DateTime dateTime = DateTime.Now; + // string time = DateTime.Now.ToString( + // "yyyyMMddHHmmss", DateTimeFormatInfo.InvariantInfo); + // var dirpath = System.IO.Path.Combine(Environment.CurrentDirectory, "wwwroot", "TempFile"); + // if (!Directory.Exists(dirpath)) + // { + // Directory.CreateDirectory(dirpath); + // } + // var filepath = System.IO.Path.Combine(dirpath, time); + // string path = dirpath + @"/" + time + ".txt"; + // base64 = str2Base64(text); + // byte[] bytes = Convert.FromBase64String(base64); + // System.IO.FileStream stream = new System.IO.FileStream(path, System.IO.FileMode.CreateNew); + // System.IO.BinaryWriter writer = new System.IO.BinaryWriter(stream); + // writer.Write(bytes, 0, bytes.Length); + // writer.Close(); + // Log.Info("文字语音播报临时文件: " + path + ""); + // string id = Guid.NewGuid().ToString(); + // //通过文件发送数据 + // com.NewMethod(path, ".txt"); + + // //正式执行命令 + // string url = "http://192.168.0.34:92/api/UploadFP/UploadFP"; + // Regex re = new Regex(@"(((?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(:[0-9]+)?|(?:ww‌​w.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?‌​(?:[\w]*))?)"); + // MatchCollection mc = re.Matches(url);//获取的是一个数组 + // string pdfurl = mc[0].ToString() + "://" + mc[1].ToString() + urlpath; + // paramsString = "{\"callback\":\"" + callback + "\",\"bsid\":\"" + id + "\",\"type\":\"3\",\"param\":{\"text\":\"" + pdfurl + "\",\"ispaye\":\"" + ispaye + "\"}}"; + // Log.Info("文字语音播报: " + paramsString + ""); + // base64 = str2Base64(paramsString); + //} + //string str = com.SendData(base64, callback); + //return Base64str2(str); + if (ispaye) + { + Task.Run(() => + { + if (Typrocess != null) + { + // 如果进程还在运行 + if (!Typrocess.HasExited) + { + // 发送SIGTERM信号来停止进程 + Typrocess.Kill(); + // 等待进程真正停止 + Typrocess.WaitForExit(); + } + } + }); + string result = "{\"callback\":\"" + callback + "\",\"message\":\"success\",\"code\":\"200\",\"status\":true,\"data\":" + "停止播放成功" + "}"; + SubmitLogs(result, "payleText"); + return result; + } + else + { + Task.Run(() => + { + //形成语音 + tempWav = GenerateWavFromText(text); + //开始播放 + string command = $"sox {tempWav} -d"; + ShllCommad(command); + }); + string result = "{\"callback\":\"" + callback + "\",\"message\":\"success\",\"code\":\"200\",\"status\":true,\"data\":" + "开始播放" + "}"; + SubmitLogs(result, "payleText"); + return result; + } + } + } + catch (Exception ex) + { + Log.Error("文字语音播报异常2: " + ex.Message + ""); + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "payleText"); + return result; + } + } + + /// + /// 发送短信 + /// + /// + /// + /// + [JSFunction] + public string SendSSM(string content, string phone) + { + try + { + if (!isFuncisFuncObject("SendSSM")) + { + Utils.MessagesBox("发送短信设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "发送短信设备未授权使用" + "\"}"; + } + else + { + string paramsString = "{\"callback\":\"" + callback + "\",\"type\":\"4\",\"param\":{\"content\":\"" + content + "\",\"phone\":\"" + phone + "\"}}"; + Log.Info("发送短信: " + paramsString + ""); + string base64 = str2Base64(paramsString); + string str = com.SendData(base64, callback); + string result = Base64str2(str); + SubmitLogs(result, "SendSSM"); + return result; + } + } + catch (Exception ex) + { + Log.Error("发送短信异常: " + ex.Message + ""); + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "SendSSM"); + return result; + } + } + + /// + /// 打开高拍仪并且进行快速扫描文件 + /// + /// + /// + [JSFunction] + public string openCamera(string url) + { + try + { + if (!isFuncisFuncObject("openCamera")) + { + Utils.MessagesBox("高拍仪设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "高拍仪设备未授权使用" + "\"}"; + } + else + { + string paramsString = "{\"callback\":\"" + callback + "\",\"type\":\"5\",\"param\":{\"url\":\"" + url + "\"}}"; + Log.Info("打开高拍仪并且进行快速扫描文件: " + paramsString + ""); + string base64 = str2Base64(paramsString); + string str = com.SendData(base64, callback); + string data = Base64str2(str); + if (data == "400") + { + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "获取签字失败" + "\"}"; + SubmitLogs(result, "openCamera"); + return result; + } + else + { + string result = "{\"callback\":\"" + callback + "\",\"message\":\"success\",\"code\":\"200\",\"status\":true,\"suffix\":\"jpg\",\"data\":\"" + str + "\"}"; + SubmitLogs(result, "openCamera"); + return result; + } + } + } + catch (Exception ex) + { + Log.Error("打开高拍仪并且进行快速扫描文件异常: " + ex.Message + ""); + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "openCamera"); + return result; + } + } + + /// + /// 打开签字版 + /// + /// + /// + [JSFunction] + public string OpenSign(string paramsString) + { + try + { + if (!isFuncisFuncObject("OpenSign")) + { + Utils.MessagesBox("签字版设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "签字版设备未授权使用" + "\"}"; + } + else + { + paramsString = "{\"callback\":\"" + callback + "\",\"type\":\"6\",\"param\":{\"data\":\"" + "" + "\"}}"; + Log.Info("打开签字版: " + paramsString + ""); + string base64 = str2Base64(paramsString); + string str = com.SendData(base64, callback); + string result = Base64str2(str); + SubmitLogs(result, "OpenSign"); + return result; + } + } + catch (Exception ex) + { + Log.Error("打开签字版异常: " + ex.Message + ""); + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "OpenSign"); + return result; + } + } + + /// + /// 关闭签字版 + /// + /// + /// + [JSFunction] + public string CloseSign(string paramsString) + { + try + { + if (!isFuncisFuncObject("OpenSign")) + { + Utils.MessagesBox("签字版设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "签字版设备未授权使用" + "\"}"; + } + else + { + paramsString = "{\"callback\":\"" + callback + "\",\"type\":\"7\",\"param\":{\"data\":\"" + "" + "\"}}"; + Log.Info("关闭签字版: " + paramsString + ""); + string base64 = str2Base64(paramsString); + string str = com.SendData(base64, callback); + string result = Base64str2(str); + SubmitLogs(result, "OpenSign"); + return result; + } + } + catch (Exception ex) + { + Log.Error("关闭签字版异常: " + ex.Message + ""); + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "OpenSign"); + return result; + } + } + + /// + /// 获取签字版数据 + /// + /// + /// + [JSFunction] + public string GetSignData(string url) + { + try + { + if (!isFuncisFuncObject("OpenSign")) + { + Utils.MessagesBox("签字版设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "签字版设备未授权使用" + "\"}"; + } + else + { + string paramsString = "{\"callback\":\"" + callback + "\",\"type\":\"8\",\"param\":{\"url\":\"" + url + "\"}}"; + Log.Info("获取签字版数据: " + paramsString + ""); + string base64 = str2Base64(paramsString); + string str = com.SendData(base64, callback); + string data = Base64str2(str); + if (data == "400") + { + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "获取签字失败" + "\"}"; + SubmitLogs(result, "OpenSign"); + return result; + } + else + { + string result = "{\"callback\":\"" + callback + "\",\"message\":\"success\",\"code\":\"200\",\"status\":true,\"suffix\":\"png\",\"data\":\"" + str + "\"}"; + SubmitLogs(result, "OpenSign"); + return result; + } + } + } + catch (Exception ex) + { + Log.Error("获取签字版数据异常: " + ex.Message + ""); + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "OpenSign"); + return result; + } + } + + /// + /// 开始录音、取消录音、结束录音 + /// sudo apt-get update + /// sudo apt-get install alsa-utils + /// + /// + /// + /// + [JSFunction] + public string SoundRecording(bool isopen, string url) + { + try + { + if (!isFuncisFuncObject("SoundRecording")) + { + Utils.MessagesBox("录音设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "录音设备未授权使用" + "\"}"; + } + else + { + //结束录音上传文件 + if (!string.IsNullOrEmpty(url) && !isopen) + { + if (StopRecording()) + { + Task.Run(async () => + { + UploadInfo(url, srpath); + }); + @event2.WaitOne(); + Regex re = new Regex(@"(((?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(:[0-9]+)?|(?:ww‌​w.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?‌​(?:[\w]*))?)"); + MatchCollection mc = re.Matches(url);//获取的是一个数组 + string reurl = mc[0].ToString() + "://" + mc[1].ToString() + urlpath; + string result = "{\"callback\":\"" + callback + "\",\"message\":\"success\",\"code\":\"200\",\"status\":true,\"suffix\":\"wav\",\"data\":\"" + reurl + "\"}"; + SubmitLogs(result, "SoundRecording"); + return result; + } + else + { + string result = "{\"callback\":\"" + callback + "\",\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"suffix\":\"wav\",\"data\":\"" + "结束录音失败" + "\"}"; + SubmitLogs(result, "SoundRecording"); + return result; + } + } + else if (isopen)//开始录音 + { + if (StartRecording()) + { + string result = "{\"callback\":\"" + callback + "\",\"message\":\"success\",\"code\":\"200\",\"status\":true,\"suffix\":\"wav\",\"data\":\"" + "开始录音" + "\"}"; + SubmitLogs(result, "SoundRecording"); + return result; + } + else + { + string result = "{\"callback\":\"" + callback + "\",\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"suffix\":\"wav\",\"data\":\"" + "录音失败" + "\"}"; + SubmitLogs(result, "SoundRecording"); + return result; + } + } + else //取消录音 + { + if (StopRecording()) + { + srpath = string.Empty; + string result = "{\"callback\":\"" + callback + "\",\"message\":\"success\",\"code\":\"200\",\"status\":true,\"suffix\":\"wav\",\"data\":\"" + "取消录音" + "\"}"; + SubmitLogs(result, "SoundRecording"); + return result; + } + else + { + srpath = string.Empty; + string result = "{\"callback\":\"" + callback + "\",\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"suffix\":\"wav\",\"data\":\"" + "取消录音失败" + "\"}"; + SubmitLogs(result, "SoundRecording"); + return result; + } + + } + } + } + catch (Exception ex) + { + Log.Error("开始录音、取消录音、结束录音异常: " + ex.Message + ""); + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "SoundRecording"); + return result; + } + } + + /// + /// 根据文件地址在线打印 + /// + /// + /// + [JSFunction] + public string PrintFile(string url, string ext) + { + try + { + if (!isFuncisFuncObject("PrintFile")) + { + Utils.MessagesBox("打印机设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "打印机设备未授权使用" + "\"}"; + } + else + { + Task.Run(async () => + { + DateTime dateTime = DateTime.Now; + string time = DateTime.Now.ToString( + "yyyyMMddHHmmss", DateTimeFormatInfo.InvariantInfo); + var dirpath = System.IO.Path.Combine(Environment.CurrentDirectory, "wwwroot", "PrintFile"); + if (!Directory.Exists(dirpath)) + { + Directory.CreateDirectory(dirpath); + } + var filepath = System.IO.Path.Combine(dirpath, time); + string path = dirpath + "\\" + time + "." + ext; + WebRequest request = WebRequest.Create(url); + WebResponse response = request.GetResponse(); + using (Stream stream = response.GetResponseStream()) + { + using (FileStream fileStream = new FileStream(path, FileMode.Create)) + { + stream.CopyTo(fileStream); + } + } + response.Close(); + Log.Info("根据文件base64打印: " + path + ""); + string command = $"lp -d {PrinterName} {path}"; + ShllCommad(command); + }); + + //string id = Guid.NewGuid().ToString(); + ////通过文件发送数据 + //com.NewMethod(path, id); + //string paramsString = "{\"callback\":\"" + callback + "\",\"bsid\":\"" + id + "\",\"type\":\"9\",\"param\":{\"url\":\"" + url + "\",\"ext\":\"" + ext + "\"}}"; + //Log.Info("根据文件地址在线打印: " + paramsString + ""); + //string base64_1 = str2Base64(paramsString); + //string str = com.SendData(base64_1, callback); + //return Base64str2(str); + string result = "{\"callback\":\"" + callback + "\",\"message\":\"success\",\"code\":\"200\",\"status\":true,\"data\":\"" + "打印成功" + "\"}"; + SubmitLogs(result, "PrintFile"); + return result; + } + } + catch (Exception ex) + { + Log.Error("根据文件地址在线打印异常: " + ex.Message + ""); + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "PrintFile"); + return result; + } + + } + + /// + /// 根据文件base64打印 + /// + /// + /// + /// + [JSFunction] + public string PrintBase64(string url, string base64, string ext) + { + try + { + if (!isFuncisFuncObject("PrintBase64")) + { + Utils.MessagesBox("打印机设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "打印机设备未授权使用" + "\"}"; + } + else + { + Task.Run(async () => + { + DateTime dateTime = DateTime.Now; + string time = DateTime.Now.ToString( + "yyyyMMddHHmmss", DateTimeFormatInfo.InvariantInfo); + var dirpath = System.IO.Path.Combine(Environment.CurrentDirectory, "wwwroot", "PrintFile"); + if (!Directory.Exists(dirpath)) + { + Directory.CreateDirectory(dirpath); + } + var filepath = System.IO.Path.Combine(dirpath, time); + string path = dirpath + @"/" + time + "." + ext; + byte[] bytes = Convert.FromBase64String(base64); + System.IO.FileStream stream = new System.IO.FileStream(path, System.IO.FileMode.CreateNew); + System.IO.BinaryWriter writer = new System.IO.BinaryWriter(stream); + writer.Write(bytes, 0, bytes.Length); + writer.Close(); + Log.Info("根据文件base64打印: " + path + ""); + string command = $"lp -d {PrinterName} {path}"; + ShllCommad(command); + }); + //string id = Guid.NewGuid().ToString(); + ////通过文件发送数据 + //com.NewMethod(path, ext); + //Regex re = new Regex(@"(((?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(:[0-9]+)?|(?:ww‌​w.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?‌​(?:[\w]*))?)"); + //MatchCollection mc = re.Matches(url);//获取的是一个数组 + //string pdfurl = mc[0].ToString() + "://" + mc[1].ToString() + urlpath; + //Log.Info("根据文件base64打印: " + pdfurl + ""); + //try + //{ + // string paramsString = "{\"callback\":\"" + callback + "\",\"bsid\":\"" + id + "\",\"type\":\"9\",\"param\":{\"url\":\"" + pdfurl + "\",\"ext\":\"" + ext + "\"}}"; + // Log.Info("根据文件base64打印: " + paramsString + ""); + // string base64_1 = str2Base64(paramsString); + // string str = com.SendData(base64_1, callback); + // return Base64str2(str); + //} + //catch (Exception ex) + //{ + // Log.Error("根据文件base64打印异常: " + ex.Message + ""); + // return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + //} + string result = "{\"callback\":\"" + callback + "\",\"message\":\"success\",\"code\":\"200\",\"status\":true,\"data\":\"" + "打印成功" + "\"}"; + SubmitLogs(result, "PrintBase64"); + return result; + } + } + catch (Exception ex) + { + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "PrintBase64"); + return result; + } + } + + /// + /// 播放音频文件 + /// + /// + /// + public static WaveOutEvent player = null; + public static AudioFileReader audioFileReader = null; + public static string WaveOutPath = string.Empty; + [JSFunction] + public string PalyFile(string url, bool ispaly) + { + try + { + if (!isFuncisFuncObject("PalyFile")) + { + Utils.MessagesBox("音频设备未授权使用"); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + "音频设备未授权使用" + "\"}"; + } + else + { + if (ispaly) + { + Task.Run(() => + { + Uri uri = new Uri(url); + //返回 123.xlsx + var filename = HttpUtility.UrlDecode(uri.Segments.Last()); + //获取文件后缀 + string ext = System.IO.Path.GetExtension(filename); + DateTime dateTime = DateTime.Now; + string time = DateTime.Now.ToString( + "yyyyMMddHHmmss", DateTimeFormatInfo.InvariantInfo); + var dirpath = System.IO.Path.Combine(Environment.CurrentDirectory, "wwwroot", "WaveOutFile"); + if (!Directory.Exists(dirpath)) + { + Directory.CreateDirectory(dirpath); + } + string path = dirpath + @"/" + time + "." + ext; + WebRequest request = WebRequest.Create(url); + WebResponse response = request.GetResponse(); + using (Stream stream = response.GetResponseStream()) + { + using (FileStream fileStream = new FileStream(WaveOutPath, FileMode.Create)) + { + stream.CopyTo(fileStream); + } + } + response.Close(); + + //开始播放 + string command = $"sox {path} -d"; + ShllCommad(command); + }); + string result = "{\"callback\":\"" + callback + "\",\"message\":\"success\",\"code\":\"200\",\"status\":true,\"data\":" + "开始播放" + "}"; + SubmitLogs(result, "PalyFile"); + return result; + } + else + { + Task.Run(() => + { + if (Typrocess != null) + { + // 如果进程还在运行 + if (!Typrocess.HasExited) + { + // 发送SIGTERM信号来停止进程 + Typrocess.Kill(); + // 等待进程真正停止 + Typrocess.WaitForExit(); + } + } + }); + string result = "{\"callback\":\"" + callback + "\",\"message\":\"success\",\"code\":\"200\",\"status\":true,\"data\":" + "停止播放成功" + "}"; + SubmitLogs(result, "PalyFile"); + return result; + } + } + } + catch (Exception ex) + { + string result = "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + SubmitLogs(result, "PalyFile"); + return result; + } + } + + /// + /// 播放音频公共方法 + /// + /// + private void NewMethod(string url) + { + Uri uri = new Uri(url); + //返回 123.xlsx + var filename = HttpUtility.UrlDecode(uri.Segments.Last()); + //获取文件后缀 + string ext = System.IO.Path.GetExtension(filename); + DateTime dateTime = DateTime.Now; + string time = DateTime.Now.ToString( + "yyyyMMddHHmmss", DateTimeFormatInfo.InvariantInfo); + var dirpath = System.IO.Path.Combine(Environment.CurrentDirectory, "wwwroot", "WaveOutFile"); + if (!Directory.Exists(dirpath)) + { + Directory.CreateDirectory(dirpath); + } + string path = dirpath + @"/" + time + "." + ext; + WebRequest request = WebRequest.Create(url); + WebResponse response = request.GetResponse(); + using (Stream stream = response.GetResponseStream()) + { + using (FileStream fileStream = new FileStream(WaveOutPath, FileMode.Create)) + { + stream.CopyTo(fileStream); + } + } + response.Close(); + + + using (player = new WaveOutEvent()) + { + player.PlaybackStopped += waveOut_PlaybackStopped; + using (audioFileReader = new AudioFileReader(WaveOutPath)) + { + // 创建一个增益效果器对象 + var volumeProvider = new VolumeSampleProvider(audioFileReader.ToSampleProvider()); + // 将音频文件添加到WaveOutEvent对象中 + player.Init(volumeProvider); + // 设置音量增益为2倍 + volumeProvider.Volume = 2.0f; + // 开始播放音频 + player.Play(); + while (player.PlaybackState == PlaybackState.Playing) + { + Thread.Sleep(1000); + } + } + } + Log.Info("开始播放"); + } + + /// + /// 播放完成 + /// + /// + /// + private void waveOut_PlaybackStopped(object sender, StoppedEventArgs e) + { + try + { + player.Stop(); + // 释放资源 + player.Dispose(); + audioFileReader.Dispose(); + player = null; + File.Delete(WaveOutPath); + Log.Info("播放完成"); + } + catch (Exception ex) + { + Log.Info("播放完成,清除本地文件异常" + ex.Message); + } + } + + /// + /// 唤醒键盘 + /// + /// + /// + [JSFunction] + public string openKey(string paramsString) + { + try + { + Task.Run(() => + { + Bash("/usr/bin/python3 /usr/bin/onboard"); + }); + return "{\"message\":\"seccse\",\"code\":\"200\",\"status\":true,\"data\":\"" + "唤醒键盘成功" + "\"}"; + } + catch (Exception ex) + { + Log.Error("唤醒键盘异常: " + ex.Message + ""); + return "{\"message\":\"fali\",\"code\":\"400\",\"status\":false,\"data\":\"" + ex.Message + "\"}"; + } + } + + /// + /// 执行文件 + /// + /// + /// + public static string Bash(string command) + { + var escapedArgs = command.Replace("\"", "\\\""); + var process = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = "/bin/bash", + Arguments = $"-c \"{escapedArgs}\"", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + } + }; + process.Start(); + string result = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + process.Dispose(); + return result; + } + + /// + /// 初始文件上传 + /// + private string urlpath = string.Empty; + private AutoResetEvent @event2 = new AutoResetEvent(false); + private async void UploadInfo(string url, string srpath) + { + var httpClient = new HttpClient(); + var uploader = new ChunkedUpload(httpClient); + urlpath = await uploader.UploadFileAsync(url, srpath); + @event2.Set(); + } + + + //上传本地文件中转 + private string urlP = string.Empty; + private AutoResetEvent @event5 = new AutoResetEvent(false); + private async void UploadInfoByFile(string url, string strpath) + { + var httpClient = new HttpClient(); + var uploader = new ChunkedUpload(httpClient); + urlP = await uploader.UploadFileAsync(url, strpath); + File.Delete(strpath); + @event5.Set(); + } + + /// + /// string 转换为 base64 + /// + /// + public static string str2Base64(string str) + { + byte[] b = System.Text.Encoding.UTF8.GetBytes(str); + string result = Convert.ToBase64String(b); + return result; + } + + /// + /// base64 转换为 string + /// + /// + public static string Base64str2(string data) + { + byte[] c = Convert.FromBase64String(data); + string result = System.Text.Encoding.UTF8.GetString(c); + Log.Info("接收返回数据:" + result); + return result; + } + + //根据程序名称杀死进程 + public static void KillProcessByName(string processName) + { + Process[] processes = Process.GetProcessesByName(processName); + + foreach (Process process in processes) + { + try + { + process.Kill(); + process.WaitForExit(); // 等待进程退出 + } + catch (Exception ex) { } + } + } + + //关闭串口 + public void CLoseCOM() + { + com.ClosePort(); + } + + /// + /// 开始录音 + /// + /// + public bool StartRecording() + { + try + { + if (recordingProcess != null) + { + // 如果进程还在运行 + if (!recordingProcess.HasExited) + { + // 发送SIGTERM信号来停止arecord进程 + recordingProcess.Kill(); + recordingProcess.WaitForExit(); // 等待进程真正停止 + } + } + string time = DateTime.Now.ToString( + "yyyyMMddHHmmss", DateTimeFormatInfo.InvariantInfo); + var dirpath = System.IO.Path.Combine(Environment.CurrentDirectory, "wwwroot", "Record"); + if (!Directory.Exists(dirpath)) + { + Directory.CreateDirectory(dirpath); + } + srpath = dirpath + "/" + time + ".wav"; + string outputFile = srpath; // 输出文件名 + string device = "default"; // 音频设备,可以通过arecord -l来列出所有设备 + // 构建arecord命令 + string command = $"arecord --device={device} --file-type=wav --duration=10 --quiet {outputFile}"; + // 启动进程 + recordingProcess = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "/bin/bash", + Arguments = $"-c \"{command}\"", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true + } + }; + recordingProcess.Start(); + return true; + } + catch (Exception ex) + { + Log.Error("开始录音异常:" + ex.Message); + return false; + } + } + + /// + /// 结束录音 + /// + public bool StopRecording() + { + try + { + // 如果进程还在运行 + if (!recordingProcess.HasExited) + { + // 发送SIGTERM信号来停止arecord进程 + recordingProcess.Kill(); + recordingProcess.WaitForExit(); // 等待进程真正停止 + } + return true; + } + catch (Exception ex) + { + Log.Error("结束录音异常:" + ex.Message); + return false; + } + } + + + public static WaveFileWriter writer; + public static StreamWriter mStreamWriter; + public static WaveInEvent waveIn = null; + private string srpath = string.Empty; + //开启录音 + private bool StateSoundRecor() + { + if (waveIn != null) + { + waveIn.StopRecording(); + waveIn.Dispose(); + writer.Close(); + mStreamWriter.Close(); + waveIn = null; + } + // 录音对象 + waveIn = new WaveInEvent(); + int sampleRate = 48000; //采样率 + int channels = 2; //录音通道数 + int bitsPerSample = 16; //位深 + WaveFormat waveFormat = new WaveFormat(sampleRate, bitsPerSample, channels); + waveIn.WaveFormat = waveFormat; //设置录音格式 + DateTime dateTime = DateTime.Now; + string time = DateTime.Now.ToString( + "yyyyMMddHHmmss", DateTimeFormatInfo.InvariantInfo); + var dirpath = System.IO.Path.Combine(Environment.CurrentDirectory, "wwwroot", "Record"); + if (!Directory.Exists(dirpath)) + { + Directory.CreateDirectory(dirpath); + } + srpath = dirpath + "/" + time + ".wav"; + string pathfile = dirpath + "/" + time + ".txt"; + // 创建WaveFileWriter对象来保存录音数据 路径在bin文件下 + writer = new WaveFileWriter(srpath, waveFormat); + //编写器 + mStreamWriter = new StreamWriter(pathfile, false, new System.Text.UTF8Encoding(false)); + // 设置录音回调函数 + int bitIndex = bitsPerSample / 8; + waveIn.DataAvailable += (sender, e) => + { + // 将录音数据写入文件 + writer.Write(e.Buffer, 0, e.BytesRecorded); + for (int i = 0; i < e.BytesRecorded / bitIndex; i++) + { + //24bit,导出的数据 + //int sample = (int)((e.Buffer[i * bitIndex + 2] << 16) | (e.Buffer[i * bitIndex + 1] << 8) | e.Buffer[i * bitIndex]); + //16bit 将两个byte数据组合成一个short数据 + short sample = (short)((e.Buffer[i * bitIndex + 1] << 8) | e.Buffer[i * bitIndex]); + mStreamWriter.Write("{0},", sample); + } + }; + try + { + //尝试打开录音设备,如果设备支持设置的WaveFormat,则能够成功打开 + waveIn.StartRecording(); + return true; + } + catch (Exception ex) + { + Log.Info("录音开启失败:" + ex.Message); + srpath = ""; + return false; + } + } + + /// + /// 停止录音、取消录音 + /// + /// + private bool StopSoundRecor() + { + + try + { + //停止录音 + waveIn.StopRecording(); + waveIn.Dispose(); + writer.Close(); + mStreamWriter.Close(); + waveIn = null; + return true; + } + catch (Exception ex) + { + Log.Info("停止录音、取消录音失败:" + ex.Message); + srpath = ""; + return false; + } + } + + /// + /// 执行命令 + /// + public static void ShllCommad(string command) + { + Log.Info("执行命令:" + command); + // 启动进程 + Typrocess = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "/bin/bash", + Arguments = $"-c \"{command}\"", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + } + }; + Typrocess.Start(); + Typrocess.WaitForExit(); + } + + /// + /// 开源文字转语音 + /// sudo apt-get install espeak + /// + /// + /// + static string GenerateWavFromText(string text) + { + string time = DateTime.Now.ToString( + "yyyyMMddHHmmss", DateTimeFormatInfo.InvariantInfo); + var dirpath = System.IO.Path.Combine(Environment.CurrentDirectory, "wwwroot", "WavFile"); + if (!Directory.Exists(dirpath)) + { + Directory.CreateDirectory(dirpath); + } + var tempFile = dirpath + "/" + time + ".wav"; + text = text.Replace("(", "").Replace(")", "、"); + ProcessStartInfo startInfo = new ProcessStartInfo + { + FileName = "espeak", + Arguments = $" -vzh -s 150 \"{text}\" -w {tempFile}", // 设置语速为150,输出为WAV文件 + UseShellExecute = false, + RedirectStandardOutput = true, + CreateNoWindow = true + }; + using (Process process = Process.Start(startInfo)) + { + process.WaitForExit(); // 等待进程结束 + process.Close(); + process.Dispose(); + } + return tempFile; + } + + /// + /// 是否已获取收取 + /// + /// + /// + public bool isFuncisFuncObject(string funcName) + { + bool isFunc = false; + if (Parame.FuncObject.Count > 0) + { + foreach (Func func in Parame.FuncObject) + { + if (func.Interfaceaddress.Contains(funcName)) + { + isFunc = true; + break; + } + } + } + return isFunc; + } + + //提交设备操作日志 + public static void SubmitLogs(string Describer, string funcName) + { + Task.Run(async () => + { + string ApiId = string.Empty; + string Name = string.Empty; + string Interfaceaddress = string.Empty; + bool isFunc = false; + if (Parame.FuncObject.Count > 0) + { + foreach (Func func in Parame.FuncObject) + { + if (func.Interfaceaddress.Contains(funcName)) + { + isFunc = true; + ApiId = func.Id; + Name = func.Platform; + Interfaceaddress = func.Interfaceaddress; + break; + } + } + } + if (isFunc) + { + var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Post, $"{Parame.apiUrl}/api/Interface/JournalAdd"); + var content = new StringContent("{\"Name\": \"" + Name + "\",\"Describer\": \"" + str2Base64(Describer) + "\", \"Interfaceaddress\": \"" + Interfaceaddress + "\", \"ApiId\": \"" + ApiId + "\"}", null, "application/json"); + request.Content = content; + var response = await client.SendAsync(request); + if (response.StatusCode.ToString() == "200") + { + response.EnsureSuccessStatusCode(); + var body = await response.Content.ReadAsStringAsync(); + Log.Info(body); + } + } + }); + } + + + } +} diff --git a/CPF_Cef/Parame.cs b/CPF_Cef/Parame.cs new file mode 100644 index 0000000..834aeb6 --- /dev/null +++ b/CPF_Cef/Parame.cs @@ -0,0 +1,25 @@ +using AKS.EnterpriseLibrary.WebBrowser; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AksWebBrowser +{ + public class Parame + { + public static CusWebBrowser webBrowser { get; set; } + public static List FuncObject { get; set; } + //接口地址 + public static string apiUrl = "http://192.168.0.34:92"; + //授权key + public static string key = "1"; + } + public struct Func + { + public string Id { get; set; } + public string Platform { get; set; } + public string Interfaceaddress { get; set; } + }; +} diff --git a/CPF_Cef/Program.cs b/CPF_Cef/Program.cs new file mode 100644 index 0000000..9f15228 --- /dev/null +++ b/CPF_Cef/Program.cs @@ -0,0 +1,38 @@ +using CPF.Cef; +using CPF.Linux;//如果需要支持Linux才需要 +using CPF.Mac;//如果需要支持Mac才需要 +using CPF.Platform; +using CPF.Skia; +using CPF.Windows; +using System; + +namespace AKS.EnterpriseLibrary.WebBrowser +{ + class Program + { + [STAThread] + static void Main(string[] args) + { + Application.Initialize( + (OperatingSystemType.Windows, new WindowsPlatform(), new SkiaDrawingFactory()) + , (OperatingSystemType.OSX, new MacPlatform(), new SkiaDrawingFactory())//如果需要支持Mac才需要 + , (OperatingSystemType.Linux, new LinuxPlatform(), new SkiaDrawingFactory())//如果需要支持Linux才需要 + ); + + CefRuntime.Load(); + var mainArgs = new CpfCefMainArgs(args); + var app = new CpfCefApp(); + var exitCode = CefRuntime.ExecuteProcess(mainArgs, app, IntPtr.Zero); + if (exitCode != -1) + { + return; + } + CefRuntime.Initialize(mainArgs, new CefSettings{ }, app, IntPtr.Zero); + + var model = new MainModel(); + Application.Run(new FrmMain { DataContext = model, CommandContext = model }); + + CefRuntime.Shutdown(); + } + } +} diff --git a/CPF_Cef/Recent.ico b/CPF_Cef/Recent.ico new file mode 100644 index 0000000000000000000000000000000000000000..a05686b39c241e3b825e14e99bbf7413468cce1e GIT binary patch literal 83695 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 + /// 显示任务栏 + /// + public static void Showtask() + { + ShowWindow(FindWindow("Shell_TrayWnd", null), SW_RESTORE); + } + /// + /// 隐藏任务栏 + /// + public static void Hidetask() + { + ShowWindow(FindWindow("Shell_TrayWnd", null), SW_HIDE); + } + + + + // 设置窗体的显示状态 + [DllImport("user32.dll", SetLastError = true)] + public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, + int X, int Y, int cx, int cy, uint uFlags); + + // 窗体的句柄 + [DllImport("user32.dll")] + public static extern IntPtr GetForegroundWindow(); + + public const uint SWP_SHOWWINDOW = 0x0040; + public const uint SWP_NOSIZE = 0x0001; + public const uint SWP_NOMOVE = 0x0002; + public const uint TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW; + } +} diff --git a/CPF_Cef/StyleSheet.css b/CPF_Cef/StyleSheet.css new file mode 100644 index 0000000..db1d459 --- /dev/null +++ b/CPF_Cef/StyleSheet.css @@ -0,0 +1,473 @@ + +@media windows { + * { + FontFamily: '微软雅黑'; /*不同系统的字体不同,自己根据情况改或者使用内嵌字体*/ + } +} + +@media osx { + * { + FontFamily: '苹方-简'; + } +} + +@media linux { + * { + FontFamily: '文泉驿正黑'; + } +} +/*设置窗体标题栏背景颜色圆角*/ +/*#caption { + IsAntiAlias: true; + Background: #2c2c2c; + CornerRadius:5,5,0,0; +} +#frame { + CornerRadius: 5; + IsAntiAlias: true; +}*/ +Button { + BorderFill: #DCDFE6; + IsAntiAlias: True; + CornerRadius: 4,4,4,4; + Background: #FFFFFF; +} + + Button[IsMouseOver=true] { + BorderFill: rgb(198,226,255); + Background: rgb(236,245,255); + Foreground: rgb(64,158,255); + } + + Button[IsPressed=true] { + BorderFill: rgb(58,142,230); + } + + Button.primary { + BorderFill: rgb(64,158,255); + CornerRadius: 4,4,4,4; + Background: rgb(64,158,255); + Foreground: #FFFFFF; + } + + Button.primary[IsMouseOver=true] { + BorderFill: rgb(102,177,255); + Background: rgb(102,177,255); + Foreground: #FFFFFF; + } + + Button.primary[IsPressed=true] { + BorderFill: rgb(58,142,230); + Background: rgb(58,142,230); + } + + Button.success { + BorderFill: rgb(103,194,58); + CornerRadius: 4,4,4,4; + Background: rgb(103,194,58); + Foreground: #FFFFFF; + } + + Button.success[IsMouseOver=true] { + BorderFill: rgb(133,206,97); + Background: rgb(133,206,97); + Foreground: #FFFFFF; + } + + Button.success[IsPressed=true] { + BorderFill: rgb(93,175,52); + Background: rgb(93,175,52); + } + + Button.danger { + BorderFill: rgb(245,108,108); + Background: rgb(245,108,108); + CornerRadius: 4,4,4,4; + Foreground: #FFFFFF; + } + + Button.danger[IsMouseOver=true] { + BorderFill: rgb(247,137,137); + Background: rgb(247,137,137); + Foreground: #FFFFFF; + } + + Button.danger[IsPressed=true] { + BorderFill: rgb(221,97,97); + Background: rgb(221,97,97); + } + + +TextBox, .textBox, DatePicker { + Background: #fff; + IsAntiAlias: true; + BorderFill: #DCDFE6; + CornerRadius: 4,4,4,4; + BorderStroke: 1; +} + + .groupPanel TextBox, DatePicker TextBox, .textBox TextBox { + BorderStroke: 0; + } + + .textBox[IsKeyboardFocusWithin=true] { + BorderFill: #1E9FFF; + } + +.singleLine { /*单行文本框*/ + AcceptsReturn: false; + HScrollBarVisibility: Hidden; + VScrollBarVisibility: Hidden; +} + + .singleLine #contentPresenter { + Padding: 3; + } + +.multiline { /*多行文本框*/ +} + + +.slotLeft { + CornerRadius: 0,4,4,0; + BorderFill: #DCDFE6; + Background: #F5F7FA; + BorderThickness: 1,0,0,0; + BorderType: BorderThickness; + MarginTop: 0; + MarginBottom: 0; + MarginRight: 0; +} + +RadioButton #radioButtonBorder { + StrokeFill: rgb(220,223,230); +} + +RadioButton[IsChecked=true] #radioButtonBorder { + StrokeFill: #1E9FFF; + Fill: #1E9FFF; +} + +RadioButton #optionMark { + StrokeFill: #1E9FFF; + Fill: #fff; +} + +CheckBox #indeterminateMark { + Fill: #1E9FFF; +} + +CheckBox #checkBoxBorder { + Background: #fff; + BorderFill: rgb(220,223,230); +} + +CheckBox[IsChecked=true] #checkBoxBorder { + Background: #1E9FFF; + BorderFill: #1E9FFF; +} + +CheckBox Polyline { + StrokeFill: #fff; +} + +.radioGroup { + BorderType: BorderThickness; + BorderFill: rgb(220,223,230); + BorderThickness: 1,1,0,1; +} + + .radioGroup RadioButton { + BorderType: BorderThickness; + BorderFill: rgb(220,223,230); + BorderThickness: 0,0,1,0; + } + + .radioGroup RadioButton #markPanel { + Visibility: Collapsed; + } + + .radioGroup RadioButton TextBlock { + Margin: 5; + } + + .radioGroup RadioButton[IsChecked=true] { + Background: rgb(64,158,255); + Foreground: #fff; + } + +.error { + Foreground: #f00; + Visibility: Collapsed; +} + + .error[DesignMode=true] { + Visibility: Visible; + } + +.twoLine[AttachedExtenstions.IsError=true] .error { + Visibility: Visible; +} + +.twoLine[AttachedExtenstions.IsError=true] .textBox { + BorderFill: #f00; +} + +ScrollBar { + Background: null; +} + + ScrollBar Thumb { + IsAntiAlias: true; + CornerRadius: 5; + } + + ScrollBar[Orientation=Horizontal] { + Height: 12; + } + + ScrollBar[Orientation=Vertical] { + Width: 12; + } + + ScrollBar #PART_LineUpButton, ScrollBar #PART_LineDownButton { + Visibility: Collapsed; + } + +ComboBox { + Background: #fff; + IsAntiAlias: true; + BorderFill: #DCDFE6; + CornerRadius: 4,4,4,4; + BorderStroke: 1; +} + + ComboBox[IsKeyboardFocusWithin=true] { + BorderFill: #1E9FFF; + } + +#DropDownPanel TextBlock { + MarginLeft: 5; + MarginTop: 2; + MarginBottom: 2; + font-size: 14; +} + +#dropDownBorder { + ShadowBlur: 2; + ShadowColor: rgba(0, 0, 0, 0.4); + BorderStroke: 0; +} + +#DropDownPanel[IsMouseOver=false] ScrollBar { + Visibility: Collapsed; +} + +#DropDownPanel ScrollBar[Orientation=Horizontal] { + Height: 10; +} + +#DropDownPanel ScrollBar[Orientation=Vertical] { + Width: 10; +} + +Slider { + IsAntiAlias: true; +} + + Slider Thumb { + IsAntiAlias: true; + Width: 16; + Height: 16; + CornerRadius: 7; + BorderFill: rgb(64,158,255); + BorderStroke: 2; + Background: #fff; + ZIndex: 1; + } + + + Slider Thumb[IsMouseOver=true] { + animation-name: sliderMouseOver; + animation-duration: 0.1s; + animation-iteration-count: 1; + animation-fill-mode: forwards; + } + + Slider #TrackBackground { + CornerRadius: 2; + Background: rgb(228,231,237); + BorderStroke: 0; + } + + Slider #decreaseRepeatButton { + Background: rgb(64,158,255); + CornerRadius: 2; + } + + Slider[Orientation=Horizontal] #decreaseRepeatButton { + Height: 4; + MarginLeft: 5; + } + + Slider[Orientation=Vertical] #decreaseRepeatButton { + Width: 4; + MarginBottom: 5; + } + +ProgressBar { + CornerRadius: 5; + IsAntiAlias: true; + BorderFill: null; + Background: rgb(235,238,245); +} + + ProgressBar #Indicator, ProgressBar #Animation { + CornerRadius: 5; + } + +NumericUpDown { + Background: #fff; + IsAntiAlias: true; + BorderFill: #DCDFE6; + CornerRadius: 4,4,4,4; + BorderStroke: 1; +} + + NumericUpDown RepeatButton { + Width: 20; + Background: rgb(245,247,250); + } + + NumericUpDown #decreaseBtn { + CornerRadius: 4,0,0,4; + } + + NumericUpDown #increaseBtn { + CornerRadius: 0,4,4,0; + } + + NumericUpDown #textBoxBorder { + BorderFill: #DCDFE6; + } + + NumericUpDown TextBox { + BorderStroke: 0; + } + +.widget { + IsAntiAlias: true; + BorderFill: #DCDFE6; + CornerRadius: 4,4,4,4; + BorderStroke: 1; +} + +.widgetHead { + Background: linear-gradient(0 0,0 100%,#F7F7F7 0,#F0F0F0 1); + BorderType: BorderThickness; + BorderThickness: 0,0,0,1; + BorderFill: #DCDFE6; +} + +DataGrid { + Foreground: #7a7a7a; +} + +DataGridCellTemplate, DataGridRow, DataGrid, .DataGridCell { + BorderFill: rgb(235,238,245); +} + +DataGridRow { + Height: 36; +} + + DataGridRow[IsMouseOver=true] { + Background: rgb(245,247,250); + } + + DataGridRow[IsSelected=true] { + Background: rgb(245,247,250); + } + +DataGridColumnTemplate { + Height: 38; + FontSize: 15; + FontStyle: Bold; + Background: #fff; + BorderFill: rgb(235,238,245); +} + +TabControl #headBorder { + Background: #fff; +} + +TabItem > Border { + BorderThickness: 0,0,0,2; +} + +TabItem[IsSelected=true] > Border { + BorderFill: #1E9FFF; +} + +TabItem[IsSelected=true] { + Foreground: #1E9FFF; +} + +TabControl[TabStripPlacement=Left] TabItem, TabControl[TabStripPlacement=Right] TabItem { + Width: 100%; + BorderType: BorderThickness; + BorderThickness: 0,0,0,1; + BorderFill: #e8e8e8; +} + + TabControl[TabStripPlacement=Left] TabItem TextBlock { + MarginRight: 0; + } + + TabControl[TabStripPlacement=Left] TabItem > Border { + Width: 100%; + } + +TabControl[TabStripPlacement=Left] #headerPanel, TabControl[TabStripPlacement=Right] #headerPanel { + Width: 100; + Background: rgb(245,247,250); +} + +.closeBtn[IsMouseOver=true] { + Fill: #171717; +} + +.placeholder { + IsHitTestVisible: false; + Foreground: "192,196,204"; + Visibility: Collapsed; +} + +.textBox[AttachedExtenstions.IsEmpty=true] .placeholder { + Visibility: Visible; +} + +.loginBox TextBox, .loginBox .placeholder { + FontSize: 16; +} + +.loginBox CheckBox { + Foreground: #757575; +} + +.searchBox Button { + CornerRadius: 0,4,4,0, +} + +#MenuPop #menuPanel > Border, #MenuPop ContextMenu > Border { + Background: #fff; + ShadowColor: rgba(0, 0, 0, 0.4); +} + +#MenuPop MenuItem[IsMouseOver=true] { + Background: #DCDFE6; +} + +ListBoxItem { + Width: 100%; +} \ No newline at end of file