From fe09fca3e77be09fed738a975ff9c5a508c2dd0d Mon Sep 17 00:00:00 2001 From: lu <99954486@qq.com> Date: 星期五, 25 十月 2024 08:31:56 +0800 Subject: [PATCH] 检验 --- DevApp/Gs.DevApp/ToolBox/UtilityHelper.cs | 737 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 719 insertions(+), 18 deletions(-) diff --git a/DevApp/Gs.DevApp/ToolBox/UtilityHelper.cs b/DevApp/Gs.DevApp/ToolBox/UtilityHelper.cs index 2b078be..24cf5c3 100644 --- a/DevApp/Gs.DevApp/ToolBox/UtilityHelper.cs +++ b/DevApp/Gs.DevApp/ToolBox/UtilityHelper.cs @@ -1,6 +1,5 @@ 锘縰sing Newtonsoft.Json.Linq; using System; -using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; @@ -10,8 +9,27 @@ using System.Resources; using System.Drawing; using Newtonsoft.Json; -using Gs.DevApp.Models; +using Gs.DevApp.Entity; using System.Windows.Forms; +using static System.Windows.Forms.Control; +using DevExpress.XtraEditors; +using DevExpress.XtraTab; +using System.Text.RegularExpressions; +using System.Collections.Generic; +using Gs.DevApp.UserControl; +using DevExpress.XtraGrid.Columns; +using DevExpress.XtraGrid; +using DevExpress.XtraGrid.Views.Grid; +using System.Configuration; +using DevExpress.XtraGrid.Views.Grid.ViewInfo; +using DevExpress.XtraTreeList.Nodes; +using DevExpress.XtraTreeList; +using System.Net.Http; +using System.Threading.Tasks; +using DevExpress.Data.Helpers; +using DevExpress.Utils.About; +using DevExpress.Office.Model; + namespace Gs.DevApp.ToolBox { @@ -20,7 +38,26 @@ /// </summary> public class UtilityHelper { - private static string WebApiUrl = System.Configuration.ConfigurationSettings.AppSettings.Get("WebApiUrl").ToString(); + private static string WebApiUrl = ConfigurationManager.AppSettings["WebApiUrl"].ToString(); + + + + public static async Task<string> UploadFileAsync(string filePath) + { + using (var httpClient = new HttpClient()) + using (var form = new MultipartFormDataContent()) + { + httpClient.DefaultRequestHeaders.Add("token", ToolBox.UtilityHelper.GetBasicAuthTicket()); + using (var fs = File.OpenRead(filePath)) + using (var streamContent = new StreamContent(fs)) + { + form.Add(streamContent, "file", Path.GetFileName(filePath)); + var response = await httpClient.PostAsync(WebApiUrl + "Upload/UploadFile", form); + string responseString = await response.Content.ReadAsStringAsync(); + return responseString; + } + } + } /// <summary> /// httpPost璁块棶鏈嶅姟 @@ -60,6 +97,7 @@ } catch (Exception ex) { + LogHelper.Debug(url, param + ":" + ex.Message); throw ex; } finally @@ -70,6 +108,16 @@ } return responseStr; } + + /// <summary> + /// 榛樿椤靛ぇ灏� + /// </summary> + /// <returns></returns> + public static int GetPageSize() + { + return int.Parse(System.Configuration.ConfigurationSettings.AppSettings.Get("PageSize").ToString()); + } + /// <summary> /// 鏍规嵁鍥剧墖鍚嶈鍙栬祫婧愭枃浠�,涓嶅甫鍚庣紑鍚� /// </summary> @@ -78,11 +126,8 @@ /// <returns></returns> public static Image GetImgFromResource(string imageName, int lay) { - // 鑾峰彇褰撳墠绋嬪簭闆� Assembly assembly = Assembly.GetExecutingAssembly(); - // 鍒涘缓璧勬簮绠$悊鍣ㄦ潵璁块棶璧勬簮 ResourceManager resourceManager = new ResourceManager("Gs.DevApp.Properties.Resources", assembly); - // 灏濊瘯鑾峰彇鍥剧墖璧勬簮 try { Image image = resourceManager.GetObject(imageName) as Image; @@ -93,12 +138,30 @@ } catch (Exception ex) { + } Image image2 = resourceManager.GetObject(lay == 1 ? "chartsshowlegend_32x32" : "linktoprevious_16x16") as Image; return image2; } + /// <summary> - /// 鐢熸垚token + /// 鍒濆鍖栦竴涓〃 + /// </summary> + /// <param name="gc"></param> + /// <param name="gv"></param> + public static void SetDefaultTable(GridControl gc, GridView gv) + { + DataTable dt = new DataTable(); + foreach (GridColumn col in gv.Columns) + { + dt.Columns.Add(col.FieldName, typeof(string)); + } + gc.BindingContext = new BindingContext(); + gc.DataSource = dt; + gc.ForceInitialize(); + } + /// <summary> + /// 鐢熸垚璁块棶鏈嶅姟鐨則oken /// </summary> /// <returns></returns> public static string GetBasicAuthTicket() @@ -109,11 +172,11 @@ return token; } /// <summary> - /// 鏍囧噯json涓茶繑鍥濺eturnModel-->table锛� + /// 鏈嶅姟杩斿洖鐨刯son杞负ReturnModel-->鍖呭惈TablePage鍒嗛〉锛� /// </summary> /// <param name="strReturn"></param> /// <returns></returns> - public static ReturnModel<PageListModel> GetTableByJson(string strReturn) + public static ReturnModel<PageListModel> ReturnToTablePage(string strReturn) { ReturnModel<PageListModel> rto = new ReturnModel<PageListModel>(); JObject json = JObject.Parse(strReturn); @@ -134,11 +197,11 @@ return rto; } /// <summary> - /// 鏍囧噯json涓茶繑鍥濺eturnModel->瀛楃涓诧紝 + /// 鏈嶅姟杩斿洖鐨刯son杩斿洖ReturnModel锛� /// </summary> /// <param name="strReturn"></param> /// <returns></returns> - public static ReturnModel<dynamic> GetDataByJson(string strReturn) + public static ReturnModel<dynamic> ReturnToDynamic(string strReturn) { ReturnModel<dynamic> rto = new ReturnModel<dynamic>(); JObject json = JObject.Parse(strReturn); @@ -147,18 +210,656 @@ rto.rtnData = json["rtnData"]; return rto; } + /// <summary> - /// 璁剧疆绯荤粺瀛椾綋澶у皬锛岀洰鍓嶅苟涓嶉�氱敤 + /// 鏈嶅姟杩斿洖鐨刯son涓茶繑鍥濺eturnModel-->浠呬粎鏈塴ist,涓嶅垎椤碉紝 /// </summary> - public static float GetFontSize = 10; - public static void SetFont(Control control) + /// <param name="strReturn"></param> + /// <returns></returns> + public static ReturnModel<DataTable> ReturnToList(string strReturn) { - float size = GetFontSize; - foreach (Control childControl in control.Controls) + ReturnModel<DataTable> rto = new ReturnModel<DataTable>(); + JObject json = JObject.Parse(strReturn); + rto.rtnCode = int.Parse(json["rtnCode"].ToString()); + rto.rtnMsg = json["rtnMsg"].ToString(); + rto.rtnData = new DataTable(); + JArray array = new JArray(); + var d = json["rtnData"]; + foreach (var a in d) { - childControl.Font = new Font(childControl.Font.FontFamily, size, childControl.Font.Style); - SetFont(childControl); + array.Add(a); + } + DataTable dt = JsonConvert.DeserializeObject<DataTable>(array.ToString()); + rto.rtnData = dt; + return rto; + } + + /// <summary> + /// 鏍规嵁瀵硅薄鎵归噺璁剧疆鏂囨湰妗嗙殑鍊� + /// </summary> + /// <param name="controls">controls:涓篻roupBox1.Controls/panel1.Controls</param> + /// <param name="dynamicObject">瀵瑰儚</param> + /// <param name="isEdt">鏄惁鍙紪杈�</param> + /// <param name="gridViews">鍏宠仈鐨刧rid</param> + public static void SetValueByObj(ControlCollection controls, dynamic dynamicObject, Boolean isEdt, List<DevExpress.XtraGrid.Views.Grid.GridView> gridViews = null) + { + isEdt = !isEdt; + if (gridViews != null) + { + foreach (DevExpress.XtraGrid.Views.Grid.GridView gv in gridViews) + { + gv.OptionsBehavior.Editable = !isEdt; + } + } + foreach (JProperty property in dynamicObject.Properties()) + { + string strName = property.Name; + string strVal = property.Value.ToString(); + try + { + string _dddddd = "txt_" + strName; + Control[] cols = controls.Find(_dddddd, true); + if (cols.Length > 0) + { + Control colType = cols[0]; + if (colType is LookUpEdit) + { + LookUpEdit txt = colType as LookUpEdit; + if (txt != null) + { + txt.EditValue = strVal; + } + txt.ReadOnly = isEdt; + continue; + } + if (colType is ImageComboBoxEdit) + { + ImageComboBoxEdit txt = colType as ImageComboBoxEdit; + for (int i = 0; i < txt.Properties.Items.Count; i++) + { + if (txt.Properties.Items[i].Description == strVal) + { + txt.ReadOnly = isEdt; + txt.SelectedIndex = i; + continue; + }; + } + continue; + } + if (colType is ComboBoxEdit) + { + ComboBoxEdit txt = colType as ComboBoxEdit; + if (txt.Properties.TextEditStyle == DevExpress.XtraEditors.Controls.TextEditStyles.DisableTextEditor) + txt.SelectedIndex = int.Parse(strVal); + else + txt.Text = strVal; + txt.ReadOnly = isEdt; + continue; + } + //涓嬫媺 + if (colType is ComboBoxEdit) + { + ComboBoxEdit txt = colType as ComboBoxEdit; + if (txt.Properties.TextEditStyle == DevExpress.XtraEditors.Controls.TextEditStyles.DisableTextEditor) + txt.SelectedIndex = int.Parse(strVal); + else + txt.Text = strVal; + txt.ReadOnly = isEdt; + continue; + } + //鑷畾涔変笅鎷� + if (colType is UcComBox) + { + UcComBox txt = colType as UcComBox; + txt.Val = strVal; + txt.Enabled = !isEdt; + continue; + } + //鏂囨湰 + if (colType is TextEdit) + { + TextEdit txt = colType as TextEdit; + if (txt != null) + txt.Text = strVal; + txt.ReadOnly = isEdt; + continue; + } + //鏁板瓧鍗� + if (colType is NumericUpDown) + { + NumericUpDown txt = colType as NumericUpDown; + if (txt != null) + txt.Text = strVal; + txt.ReadOnly = isEdt; + continue; + } + //鍗曢�� + if (colType is CheckEdit) + { + CheckEdit txt = colType as CheckEdit; + if (txt != null) + { + switch (strVal) + { + case "True": + txt.Checked = true; + break; + case "1": + txt.Checked = true; + break; + case "False": + txt.Checked = false; + break; + default: + txt.Checked = false; + break; + } + } + txt.ReadOnly = isEdt; + continue; + } + //鍗曢�� + if (colType is CheckBox) + { + CheckBox txt = colType as CheckBox; + if (txt != null) + txt.Checked = bool.Parse(strVal); + txt.Enabled = !isEdt; + continue; + } + //鏃堕棿 + if (colType is DateTimePicker) + { + DateTimePicker txt = colType as DateTimePicker; + txt.Text = strVal.ToString(); + txt.Enabled = !isEdt; + continue; + } + //Label + if (colType is Label) + { + Label txt = colType as Label; + if (_dddddd == "txt_checkStatus") + { + if (strVal.ToString() == "True") + txt.Text = "宸插鏍�"; + if (strVal.ToString() == "False") + txt.Text = "鏈鏍�"; + } + else + txt.Text = strVal.ToString(); + continue; + } + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } } } + + /// <summary> + /// 娓呯┖瀹瑰櫒閲岄潰鐨勬帶浠� + /// </summary> + /// <param name="controls">controls:涓篻roupBox1.Controls/panel1.Controls</param> + /// <param name="isEdt">鏄惁鍙紪杈�</param> + /// <param name="gridViews">鍏宠仈鐨刧rid</param> + public static void CleanValueByControl(ControlCollection controls, Boolean isEdt, List<DevExpress.XtraGrid.Views.Grid.GridView> gridViews = null) + { + isEdt = !isEdt; + if (gridViews != null) + { + foreach (DevExpress.XtraGrid.Views.Grid.GridView gv in gridViews) + { + gv.OptionsBehavior.Editable = !isEdt; + } + } + foreach (Control ctrl in controls) + { + //澶氳鏂囨湰 + if (ctrl is MemoEdit) + { + MemoEdit txt = ctrl as MemoEdit; + txt.Text = ""; + txt.ReadOnly = isEdt; + continue; + } + //涓嬫媺 + if (ctrl is ComboBoxEdit) + { + ComboBoxEdit txt = ctrl as ComboBoxEdit; + if (txt.Properties.TextEditStyle == DevExpress.XtraEditors.Controls.TextEditStyles.DisableTextEditor) + txt.SelectedIndex = 0; + else + txt.Text = ""; + txt.ReadOnly = isEdt; + continue; + } + //鑷畾涔夌殑涓嬫媺 + if (ctrl is UcComBox) + { + UcComBox txt = ctrl as UcComBox; + txt.Val = "0"; + txt.Enabled = !isEdt; + continue; + } + //鏂囨湰 + if (ctrl is TextEdit) + { + TextEdit txt = ctrl as TextEdit; + txt.Text = ""; + txt.ReadOnly = isEdt; + continue; + } + //鏃堕棿 + if (ctrl is DateTimePicker) + { + DateTimePicker txt = ctrl as DateTimePicker; + txt.Enabled = !isEdt; + continue; + } + //鍗曢�� + if (ctrl is CheckBox) + { + CheckBox txt = ctrl as CheckBox; + txt.Checked = false; + txt.Enabled = !isEdt; + continue; + } + } + } + + /// <summary> + /// 绂佺敤鎴栧惎鐢ㄥ鍣ㄩ噷闈㈢殑鎺т欢 + /// </summary> + /// <param name="controls">controls:涓篻roupBox1.Controls/panel1.Controls</param> + /// <param name="isEdt"></param> + /// <param name="gridViews"></param> + public static void ChangeEnableByControl(ControlCollection controls, Boolean isEdt, List<DevExpress.XtraGrid.Views.Grid.GridView> gridViews = null) + { + isEdt = !isEdt; + if (gridViews != null) + { + foreach (DevExpress.XtraGrid.Views.Grid.GridView gv in gridViews) + { + gv.OptionsBehavior.Editable = !isEdt; + } + } + foreach (Control ctrl in controls) + { + //鏂囨湰 + if (ctrl is TextEdit) + { + TextEdit txt = ctrl as TextEdit; + txt.ReadOnly = isEdt; + continue; + } + //鏁板瓧鍗� + if (ctrl is NumericUpDown) + { + NumericUpDown txt = ctrl as NumericUpDown; + txt.ReadOnly = isEdt; + continue; + } + //涓嬫媺 + if (ctrl is UcComBox) + { + UcComBox txt = ctrl as UcComBox; + txt.Val = "0"; + txt.Enabled = !isEdt; + continue; + } + //鏃ユ湡 + if (ctrl is DateTimePicker) + { + ctrl.Enabled = !isEdt; + continue; + } + //鍗曢�� + if (ctrl is CheckBox) + { + ctrl.Enabled = !isEdt; + continue; + } + } + } + + /// <summary> + ///鍒囨崲閫夐」鍗� + /// </summary> + /// <param name="tabControl">閫夐」鍗″鍣�</param> + /// <param name="idx">浠�0寮�濮嬶紝濡傛灉鏄�999锛屽垯鍏ㄩ儴鍙敤</param> + public static void JumpToTab(XtraTabControl tabControl, int idx) + { + if (idx == 999) + { + for (int i = 0; i < tabControl.TabPages.Count; i++) + { + tabControl.TabPages[i].PageEnabled = true; + } + tabControl.SelectedTabPageIndex = tabControl.TabPages.Count - 1; + return; + } + for (int i = 0; i < tabControl.TabPages.Count; i++) + { + tabControl.TabPages[i].PageEnabled = false; + } + tabControl.TabPages[idx].PageEnabled = true; + tabControl.SelectedTabPageIndex = idx; + } + + /// <summary> + /// TreeView鑱斿姩閫夋嫨 + /// </summary> + /// <param name="e"></param> + public static void TreeViewCheck(TreeViewEventArgs e) + { + try + { + if (e.Node.Nodes.Count > 0) + { + foreach (TreeNode tn in e.Node.Nodes) + { + tn.Checked = e.Node.Checked; + foreach (TreeNode x in tn.Nodes) + { + x.Checked = e.Node.Checked; + foreach (TreeNode y in x.Nodes) + { + y.Checked = e.Node.Checked; + } + } + } + } + if (e.Node.Checked == true && e.Node.Parent != null && e.Node.Parent.Nodes.Count > 0) + { + TreeNode currentNode = e.Node; + while (currentNode != null) + { + currentNode.Checked = e.Node.Checked; + currentNode = currentNode.Parent; + } + } + } + catch (Exception ex) + { + ToolBox.MsgHelper.ShowError(ex.Message); + } + } + + /// <summary> + /// 杞┘宄板懡鍚� + /// </summary> + /// <param name="input"></param> + /// <returns></returns> + public static string ToCamelCase(string strItem) + { + //濡傛灉鍖呭惈灏忓啓锛屼絾涓嶅寘鍚笅鍒掔嚎 + bool hasLowercase = Regex.IsMatch(strItem, @"[a-z]"); + if (hasLowercase && !strItem.Contains("_")) + { + char[] chars = strItem.ToCharArray(); + chars[0] = char.ToLower(chars[0]); + return new string(chars); + } + string[] strItems = strItem.ToLower().Split('_'); + string strItemTarget = strItems[0]; + for (int j = 1; j < strItems.Length; j++) + { + string temp = strItems[j].ToString(); + string temp1 = temp[0].ToString().ToUpper(); + string temp2 = ""; + temp2 = temp1 + temp.Remove(0, 1); + strItemTarget += temp2; + } + return strItemTarget; + + } + + /// <summary> + /// 璇诲彇grid鐨勫綋鍓嶈 + /// </summary> + /// <param name="xtraTabControl1"></param> + /// <param name="lbGuid">缂栬緫妗嗕腑鐨勪富閿悕</param> + /// <param name="txtName">缂栬緫妗嗕腑鐨勬枃鏈鍚�</param> + /// <param name="gridView1"></param> + /// <param name="SelectedTabPageIndex"></param> + /// <returns></returns> + public static (string, string) GetCurrentRow(XtraTabControl xtraTabControl1 + , System.Windows.Forms.Label lbGuid + , DevExpress.XtraEditors.TextEdit txtName + , DevExpress.XtraGrid.Views.Grid.GridView gridView1 + , int SelectedTabPageIndex = 1) + { + string _strGuid = ""; + string _strName = ""; + if (xtraTabControl1.SelectedTabPageIndex == SelectedTabPageIndex && lbGuid.Text.Length > 10) + { + _strGuid = lbGuid.Text.Trim(); + _strName = txtName.Text.Trim(); + } + else + { + DataRow dr = gridView1.GetFocusedDataRow(); + if (dr == null || string.IsNullOrEmpty(dr["guid"].ToString())) + { + + } + else + { + _strGuid = dr["guid"].ToString(); + _strName = dr[2].ToString(); + } + } + return (_strGuid, _strName); + } + /// <summary> + /// 璇诲彇TreeView鐨勫綋鍓嶈 + /// </summary> + /// <param name="xtraTabControl1"></param> + /// <param name="lbGuid"></param> + /// <param name="txtName"></param> + /// <param name="tlMenu"></param> + /// <param name="SelectedTabPageIndex"></param> + /// <returns></returns> + public static (string, string) GetCurrentRow(XtraTabControl xtraTabControl1 + , System.Windows.Forms.Label lbGuid + , DevExpress.XtraEditors.TextEdit txtName + , System.Windows.Forms.TreeView tlMenu + , int SelectedTabPageIndex = 1) + { + string _strGuid = ""; + string _strName = ""; + if (xtraTabControl1.SelectedTabPageIndex == SelectedTabPageIndex && lbGuid.Text.Length > 10) + { + _strGuid = lbGuid.Text.Trim(); + _strName = txtName.Text.Trim(); + } + else + { + TreeNode clickedNode = tlMenu.SelectedNode; + if (clickedNode != null) + { + _strGuid = clickedNode.Name.ToString(); + _strName = clickedNode.Text.Trim(); + } + } + return (_strGuid, _strName); + } + + /// <summary> + /// 璇诲彇TreeList鐨勫綋鍓嶈 + /// </summary> + /// <param name="xtraTabControl1"></param> + /// <param name="lbGuid"></param> + /// <param name="txtName"></param> + /// <param name="tlMenu"></param> + /// <param name="SelectedTabPageIndex"></param> + /// <returns></returns> + public static (string, string) GetCurrentRow(XtraTabControl xtraTabControl1 + , System.Windows.Forms.Label lbGuid + , DevExpress.XtraEditors.TextEdit txtName + , DevExpress.XtraTreeList.TreeList tlMenu + , int SelectedTabPageIndex = 1) + { + string _strGuid = ""; + string _strName = ""; + if (xtraTabControl1.SelectedTabPageIndex == SelectedTabPageIndex && lbGuid.Text.Length > 10) + { + _strGuid = lbGuid.Text.Trim(); + _strName = txtName.Text.Trim(); + } + else + { + _strGuid = tlMenu.FocusedNode.GetValue("guid").ToString(); + _strName = tlMenu.FocusedNode.GetValue(0).ToString(); + } + return (_strGuid, _strName); + } + + /// <summary> + /// 璇诲彇GridView鍙屽嚮鐨勪富閿� + /// </summary> + /// <param name="gridView1"></param> + /// <param name="e"></param> + /// <param name="colName"></param> + /// <returns></returns> + public static string GetCurrentDoubleRow(DevExpress.XtraGrid.Views.Grid.GridView gridView1 + , MouseEventArgs e + , string colName) + { + GridHitInfo info = gridView1.CalcHitInfo(e.Location); + if (info.InRow) + { + GridView view = info.View as GridView; + if (view != null) + { + DataRow row = view.GetDataRow(info.RowHandle); + if (row != null) + { + string rowGuid = (row[colName].ToString()); + return rowGuid; + } + } + } + return ""; + } + + + /// <summary> + /// 璇诲彇GridView鍙屽嚮鐨勪富閿� + /// </summary> + /// <param name="gridView1"></param> + /// <param name="e"></param> + /// <param name="colName"></param> + /// <returns></returns> + public static string GetCurrentDoubleRow(DevExpress.XtraTreeList.TreeList tlMenu + , MouseEventArgs e + , string colName) + { + TreeListHitInfo info = tlMenu.CalcHitInfo(e.Location); + if (info.Node != null) + { + TreeListNode clickedNode = tlMenu.FocusedNode; + if (clickedNode.FirstNode == null) + { + string rowGuid = clickedNode.GetValue(colName).ToString(); + return rowGuid; + } + } + return ""; + } + + } + + /// <summary> + /// 涓嬫媺妗嗘潯鐩被 + /// </summary> + public class CboItemEntity + { + public CboItemEntity() + { + } + public CboItemEntity(string val, string text) + { + this.Text = text; + this.Value = val; + } + private object _text = 0; + private object _Value = ""; + /// <summary> + /// 鏄剧ず鍊� + /// </summary> + public object Text + { + get { return this._text; } + set { this._text = value; } + } + /// <summary> + /// 瀵硅薄鍊� + /// </summary> + public object Value + { + get { return this._Value; } + set { this._Value = value; } + } + + public override string ToString() + { + return this.Text.ToString(); + } + } + + /// <summary> + /// 鏌ヨ妗� + /// </summary> + public class FilterEntity + { + /// <summary> + /// + /// </summary> + /// <param name="id">瀛楁鍚�</param> + /// <param name="idDec">瀛楁鍚嶅娉�</param> + /// <param name="oper">鎿嶄綔绗�</param> + /// <param name="operDec">鎿嶄綔绗﹀娉�</param> + /// <param name="val"></param> + public FilterEntity(string id, string idDec, string oper, string operDec, string val) + { + this.fileId = id; + this.fileIdDec = idDec; + this.fileOper = oper; + this.fileOperDec = operDec; + this.fileValue = val; + } + public string fileId + { + get; + set; + } + public string fileIdDec + { + get; + set; + } + public string fileOper + { + get; + set; + } + public string fileOperDec + { + get; + set; + } + public string fileValue + { + get; + set; + } + } + + + } -- Gitblit v1.9.3