using HotelPms.Client.Blazor.Dialog; using HotelPms.Client.Blazor.Models; using HotelPms.Client.Blazor.Util; using HotelPms.Data.Common; using HotelPms.Share.IO; using HotelPms.Share.Util; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using MudBlazor; using static HotelPms.Client.Blazor.Util.SystemEnum; namespace HotelPms.Client.Blazor.Pages.UseDetail; public partial class SaleInput : ComponentBase { [Parameter] public ViewModel.UseInput? Data { get; set; } [Parameter] public Action OnRefresh { get; set; } private System.Threading.SemaphoreSlim slimlock = new SemaphoreSlim(1, 1); private IEnumerable FeeDetailList = new List(); private IEnumerable SaleChildList = new List(); private SaleInputRow selectedItem1 = null; private List editEvents = new(); private string searchString = ""; private string RowSpaceStyle = "mt-2 pt-0 mb-0 pb-0"; private MudTable _table; private bool keyDownPreventDefault = false; private bool keyPressPreventDefault = false; private bool enterPush = false; private bool arrowUpPush = false; private EditGridFocusEventArgs m_CurrentFocusData = new EditGridFocusEventArgs(); protected override void OnInitialized() { //EnvironmentSetting.Debug($"SaleInput.OnInitialized開始:"); base.OnInitialized(); } protected override void OnAfterRender(bool firstRender) { //EnvironmentSetting.Debug($"SaleInput.OnAfterRender開始:{firstRender}"); base.OnAfterRender(firstRender); } protected override async Task OnAfterRenderAsync(bool firstRender) { await slimlock.WaitAsync(); EnvironmentSetting.Debug($"SaleInput.OnAfterRenderAsync開始:{firstRender}"); await ShowFocus(); EnvironmentSetting.Debug($"SaleInput.OnAfterRenderAsync終了:{firstRender}"); slimlock.Release(); } /// /// フォーカス移動準備 /// /// /// /// EditModeの場合、自動で次のフォーカス移動する private void SetFocus(int row, int col, EditGridFocusEventArgs.ActionType action = EditGridFocusEventArgs.ActionType.Focus) { EnvironmentSetting.Debug($"フォーカス移動準備:開始⇒Row:{row},Col:{col},Action:{action},Enabled:{m_CurrentFocusData.Enabled}"); m_CurrentFocusData.Row = row; m_CurrentFocusData.Col = col; m_CurrentFocusData.Action = action; m_CurrentFocusData.Enabled = true; EnvironmentSetting.Debug($"フォーカス移動準備:完了⇒Row:{row},Col:{col},Action:{action},Enabled:{m_CurrentFocusData.Enabled}"); } /// /// OnAfterRenderAsync時フォーカスを設定 /// private async Task ShowFocus() { try { EnvironmentSetting.Debug($"SaleInput.FocusAsync行Before:Row={m_CurrentFocusData.Row},Col={m_CurrentFocusData.Col},Action={m_CurrentFocusData.Action},Enabled={m_CurrentFocusData.Enabled}"); if (!m_CurrentFocusData.Enabled) { EnvironmentSetting.Debug($"OnAfterRenderAsync⇒return false"); return false; } m_CurrentFocusData.Enabled = false; EnvironmentSetting.Debug($"ShowFocus処理:Row={m_CurrentFocusData.Row},Col={m_CurrentFocusData.Col},Action={m_CurrentFocusData.Action},Enabled={m_CurrentFocusData.Enabled}"); if (m_CurrentFocusData.Action == EditGridFocusEventArgs.ActionType.Focus && m_CurrentFocusData.Row != -1 && m_CurrentFocusData.Col != -1) { //この時点Grid編集状態ですので、フォーカス可能になる EnvironmentSetting.Debug($"フォーカスへ開始:Row = {m_CurrentFocusData.Row}, Col = {m_CurrentFocusData.Col}"); await (Data.SaleList as List)[m_CurrentFocusData.Row].Cells[m_CurrentFocusData.Col].Ref.FocusAsync(); //ここに再度OnAfterRenderAsyncに入るが、EnabledはFalseのため、ShowFocusの動作がしない EnvironmentSetting.Debug($"フォーカスへ完了:Row = {m_CurrentFocusData.Row}, Col = {m_CurrentFocusData.Col}"); } return true; } catch(Exception ex) { EnvironmentSetting.Debug(ex.Message); return false; } } //protected override async Task OnAfterRenderAsync(bool firstRender) //{ // EnvironmentSetting.Debug($"SaleInput.OnAfterRenderAsync開始:{firstRender}"); // //await ShowFocus(); // //await JSInteropEx.DelGridCol(JSRuntime, "SaleInputTable", 11); //} protected override void OnParametersSet() { EnvironmentSetting.Debug($"SaleInput.OnParametersSet開始:"); base.OnParametersSet(); } protected override bool ShouldRender() { EnvironmentSetting.Debug($"SaleInput.ShouldRender開始:"); return base.ShouldRender(); } private async Task KeyPress(int index, SaleInputRow data, KeyboardEventArgs e) { //if (e.Key == "Enter") //{ // List list = (Data.SaleList as List); // int rowIdx = list.IndexOf(selectedItem1); // // https://bytemeta.vip/repo/MudBlazor/MudBlazor/issues/4192 // // https://github.com/MudBlazor/MudBlazor/issues/3292 // SaleInputRow newitem = (rowIdx == list.Count - 1) ? list[0] : list[rowIdx + 1]; // _table.SetSelectedItem(newitem); // _table.SetEditingItem(newitem); // await Task.Delay(50); //画面更新のため // await newitem.Cells[(int)SaleInputRow.ColType.ItemName].Ref.FocusAsync(); //OnAfterRenderAsyncへ //} if (e.Key == "Enter") { try { int rowIdx = GetRowIndex(data); Console.WriteLine($"Row:{rowIdx} Col:{index}"); Console.WriteLine("Data:" + CConvert.ToJsonText(data)); //※その時、Fields[index].Textの値まだ変っていない!!!!! string text = await data.GetInputValue(index, JSRuntime); EnvironmentSetting.Debug($"Return:{index}⇒{text}"); keyPressPreventDefault = true; //イベント中止 enterPush = true; //Enterを押した知らせ //通常チェックを行う if (!await IsValid(index, text, data, true)) { return; } if (!SetAutoNextFocus(index, true, data)) { return; } } catch (Exception ex) { OperationLog.Instance.WriteLog($"KeyPress:{ex.Message}"); } finally { Refresh("KeyPress"); //UI更新(binding値変更通知) } } else { if (index == (int)SaleInputRow.ColType.PersonCount || index == (int)SaleInputRow.ColType.UnitPrice) { if (CConvert.IsNumPress(e.Key)) { keyPressPreventDefault = false; } else { keyPressPreventDefault = true; //入力禁止(※:IMEモードで入力したものをここでは通らないため、防げない) } } else { keyPressPreventDefault = false; } enterPush = false; } } /// /// フォーカス移動 /// /// 列Index /// /// private bool SetAutoNextFocus(int index, bool isEnter, SaleInputRow data) { try { int rowIdx = GetRowIndex(data); List list = (Data.SaleList as List); if (isEnter) { if (rowIdx == -1) { return true; } //削除された場合 if (index == (int)SaleInputRow.ColType.Page) { if (rowIdx < (list.Count - 1)) { //OnAfterRenderAsync:閲覧状態且つ新規行表示⇒編集状態⇒次項目へフォーカス SetFocus(rowIdx + 1, (int)SaleInputRow.ColType.ItemName); } } else if (index == (int)SaleInputRow.ColType.DiscountSummary) { SetFocus(rowIdx, index + 2); } else { SetFocus(rowIdx, index + 1); } } else { if (index == (int)SaleInputRow.ColType.ItemName) { if (rowIdx > 0) { SetFocus(rowIdx - 1, (int)SaleInputRow.ColType.ItemName); } } else if (index == (int)SaleInputRow.ColType.SumDate) { SetFocus(rowIdx, index - 2); } else { SetFocus(rowIdx, index - 1); } } return true; } catch { return false; } } private void DetailClick(SaleInputRow data) { //Snackbar.Add("詳細表示"); List list = FeeDetailList as List; list.Clear(); list.Add(new() { Name = "本体額", Value = "10,000000" }); list.Add(new() { Name = "サービス", Value = "2,000" }); list.Add(new() { Name = "消費税", Value = "100" }); list.Add(new() { Name = "入湯税", Value = "250" }); list.Add(new() { Name = "宿泊税", Value = "400" }); list.Add(new() { Name = "合 計", Value = "16,850" }); List listChild = SaleChildList as List; listChild.Clear(); SaleChildRow row = new(); row.Cells[0] = "S"; row.Cells[1] = "101"; row.Cells[2] = "基本料金"; listChild.Add(row); row = new(); row.Cells[0] = "S"; row.Cells[1] = "101"; row.Cells[2] = "朝食"; listChild.Add(row); data.ActiveCol = (int)SaleInputRow.ColType.Detail; data.ShowDetails = !data.ShowDetails; } private void OnCommitEditButtonClicked(MouseEventArgs e) { _table.RowEditCommit?.Invoke(_table.SelectedItem); _table.OnCommitEditClick.InvokeAsync(e); _table.SetSelectedItem(null); } private async Task Save() { (Data.SaleList as List).Add(new()); //SaleInputRow newitem = (Data.SaleList as List)[5]; ////await Task.Delay(50); //_table.SetSelectedItem(newitem); //_table.SetEditingItem(newitem); await Task.Delay(50); //await newitem.Cells[(int)SaleInputRow.ColType.ItemName].Ref.FocusAsync(); } private async Task KeyDown(int index, SaleInputRow data, KeyboardEventArgs e) { if (e.Key == "ArrowUp") { try { EnvironmentSetting.Debug($"ArrowUp:{index}"); if (SetAutoNextFocus(index, false, data)) { //BlazorのBugでEnterのパラメータ変数できない! //Uncaught Error: System.ArgumentException: There is no event handler associated with this event. EventId: '290'. (Parameter 'eventHandlerId') //mkArtakMSFT modified the milestones: Next sprint planning, 6.0-preview4 on 20 Mar ← Net6.0-preview4対応だそう keyDownPreventDefault = true; arrowUpPush = true; } else { keyDownPreventDefault = false; arrowUpPush = false; } } catch (Exception ex) { OperationLog.Instance.WriteLog($"KeyDown:{ex.Message}"); } finally { Refresh("KeyDown⇒↑"); //UI更新(binding値変更通知) } } else if (e.Key == "End") { if (data.Cells[index].ShowStyle == EShowStyle.ShowList) { data.ActiveCol = index; if (index == (int)SaleInputRow.ColType.SumDate) { var parameters = new DialogParameters { ["Title"] = $"集計日選択", ["Data"] = CConvert.ToDateTime(data.Cells[index].Text) }; var dialog = DialogService.Show(string.Empty, parameters); var ret = await dialog.Result; if (!ret.Cancelled) { EnvironmentSetting.Debug(ret.Data.ToString()); data.Cells[index].Text = ret.Data.ToString(); //StateHasChanged(); } } else { data.ShowDetails = !data.ShowDetails; } } } else if (e.Key == "Delete") { if (index == (int)SaleInputRow.ColType.ItemName) { int rowIdx = GetRowIndex(data); List list = (Data.SaleList as List); bool isLastRow = rowIdx == (list.Count - 1); if (isLastRow) { return; } SetFocus(rowIdx, (int)SaleInputRow.ColType.ItemName); EnvironmentSetting.Debug($"削除前件数:{list.Count}"); list.Remove(data); EnvironmentSetting.Debug($"削除後件数:{list.Count}"); keyDownPreventDefault = false; arrowUpPush = false; } } else { keyDownPreventDefault = false; arrowUpPush = false; } } private async Task Enter(int index, SaleInputRow data, FocusEventArgs e) { await slimlock.WaitAsync(); int rowIdx = GetRowIndex(data); Console.WriteLine($"Enter⇒Row:{rowIdx} Col:{index}"); slimlock.Release(); //ここは必ずUI更新 } private async Task Leave(int index, SaleInputRow data, FocusEventArgs e) { string text = string.Empty; try { await slimlock.WaitAsync(); Console.WriteLine($"Leave⇒Start"); int rowIdx = GetRowIndex(data); text = data.GetCellText(index); Console.WriteLine($"Leave⇒Row:{rowIdx} Col:{index} enterPush:{enterPush}, text:{text}"); if (enterPush) { //Enterキーを押した時にも既にチェック済 enterPush = false; } else { //値変更したら、イベント発生 if (!data.IsValueChanged(index, text)) { //必須判断:初期表示時:OrgTextは空白のため、値変更しないまま return; } //通常チェックを行う if (!await IsValid(index, text, data, false)) { return; } //Refresh("Leave(変更あり)"); //UI更新(binding値変更通知) } } catch (Exception ex) { OperationLog.Instance.WriteLog($"Leave:{ex.Message}"); } finally { //Refresh(); //UI更新(binding値変更通知) Console.WriteLine($"Leave⇒End"); slimlock.Release(); //ここは必ずUI更新 } } private int GetRowIndex(SaleInputRow data) { return (Data.SaleList as List).IndexOf(data); } /// /// 業務チェック処理 /// /// /// /// /// /// private async Task IsValid(int index, string inputText, SaleInputRow data, bool isEnter) { int rowIdx = GetRowIndex(data); //最終行判断 List list = (Data.SaleList as List); bool isLastRow = rowIdx == (list.Count - 1); if (index == (int)SaleInputRow.ColType.ItemName) { if (!await IsValidItemName(rowIdx, inputText, data, isEnter, isLastRow, true)) { return false; } } else if(index == (int)SaleInputRow.ColType.SumDate) { } else { } return true; } private RenderFragment RenderWidget(string msg) => builder => { builder.OpenComponent(0, typeof(MessageContext)); builder.AddAttribute(1, "Text", msg); builder.CloseComponent(); }; /// /// /// /// /// /// /// Trueの場合、必ずID /// True:ID or Name False:ID /// private async Task IsValidItemName(int rowIdx, string inputText, SaleInputRow data, bool isEnter, bool isLastRow, bool isAny) { if (inputText.Trim().Length == 0) { return true; } HotelPms.Data.UseInfo.Sale curSale = data.GetSale(); bool nameChanged = false; if (isLastRow) { if (!CanCreateSale()) { return true; } } else { //変更されたかどうか bool isChanged = false; if (isAny) { //直接入力する(数字の場合、ItemIDで扱う) if (CConvert.IsWholeNumber(inputText)) { if (curSale.IsReadOnly()) { return true; } if (inputText.PadLeft(50, '0') != curSale.ItemID.PadLeft(50, '0')) { isChanged = true; } else { data.RestoreText(SaleInputRow.ColType.ItemName); } } else { if (inputText != curSale.ItemName) { isChanged = true; nameChanged = true; } } } else { //Endキーより別の科目を選択など if (inputText != curSale.ItemID) { isChanged = true; } } if (!isChanged) { return true; } //変更しなかったら } if (!RaiseValueChanged((int)SaleInputRow.ColType.ItemName, data)) { return false; } //変更イベント通知 //名称変更のみ if (nameChanged) { data.SetDataField((int)SaleInputRow.ColType.ItemName, inputText); return true; } //入力値はItemIDと処理する HotelPms.Data.Master.Item item = await MasterCore.GetItem(inputText); if (item == null) { //科目が存在しない if (isEnter) { Message.Show(RenderMessage(inputText)); } data.RestoreText(SaleInputRow.ColType.ItemName); return false; } if (isLastRow) { int saleID = await EnvironmentSetting.GetSeq(ESeqType.Sale); int taxRate = await EnvironmentSetting.GetTaxRate(Data.SelUseRoomRow.UseRoom.UseDate.ToText(), item.TaxType); curSale = Data.SelUseRoomRow.UseRoom.AddSaleByItem(saleID, taxRate, item); //選択された部屋より追加 data.DataList[rowIdx] = curSale; } else { //既存行の科目変更 if (curSale.ItemKind == (int)EItemKind.StayBase || curSale.ItemKind == (int)EItemKind.DayUseBase) { if (item.Kind != (int)EItemKind.StayBase && item.Kind != (int)EItemKind.DayUseBase) { if (isEnter) { Message.Show(RenderMessage("基本科目を入力してください。")); } data.RestoreText(SaleInputRow.ColType.ItemName); return false; } } else { if (item.Kind == (int)EItemKind.StayBase || item.Kind == (int)EItemKind.DayUseBase) { if (isEnter) { Message.Show(RenderMessage("追加科目を入力してください。")); } data.RestoreText(SaleInputRow.ColType.ItemName); return false; } } HotelPms.Data.UseInfo.UseRoom useRoom; HotelPms.Data.UseInfo.Sale newSale; if (Data.SaleDispType == ESaleDispType.Normal) { useRoom = curSale.Parent as HotelPms.Data.UseInfo.UseRoom; useRoom.RemoveSale(curSale); int saleID = await EnvironmentSetting.GetSeq(ESeqType.Sale); int taxRate = await EnvironmentSetting.GetTaxRate(useRoom.UseDate.ToText(), item.TaxType); newSale = useRoom.AddSaleByItem(saleID, taxRate, item, curSale); data.DataList.Clear(); data.DataList.Add(newSale); } else { //まとめモードの変更 List copyList = new List(); copyList.AddRange(data.DataList); foreach (HotelPms.Data.UseInfo.Sale tagSlip in copyList) { curSale = tagSlip as HotelPms.Data.UseInfo.Sale; useRoom = curSale.Parent as HotelPms.Data.UseInfo.UseRoom; useRoom.RemoveSale(curSale); //元の伝票を削除 int saleID = await EnvironmentSetting.GetSeq(ESeqType.Sale); int taxRate = await EnvironmentSetting.GetTaxRate(useRoom.UseDate.ToText(), item.TaxType); newSale = useRoom.AddSaleByItem(saleID, taxRate, item, curSale); //新伝票の追加 int orgSlipIndex = data.DataList.IndexOf(curSale); data.DataList[orgSlipIndex] = newSale; //切替える } } } data.SetDataField((int)SaleInputRow.ColType.ItemName, item.Name); //画面表示(イベントを起こすため、必ず一番最後) data.Cells[(int)SaleInputRow.ColType.ItemName].Text = item.Name; //新規行追加 if (isLastRow) { //OnAfterRenderAsync:閲覧状態且つ新規行表示⇒編集状態⇒次項目へフォーカス SetFocus(rowIdx, (int)SaleInputRow.ColType.PersonCount); EnvironmentSetting.Debug($"新規行追加⇒開始"); (Data.SaleList as List).Add(new()); EnvironmentSetting.Debug($"新規行追加⇒終了"); return false; //次の列SetAutoNextFocus行かない } return true; } /// /// 新規伝票可能かどうか /// /// private bool CanCreateSale() { return true; } /// /// 値変更 /// public bool RaiseValueChanged(int index, SaleInputRow data) { return true; } /// /// コントロール再表示 /// public void Refresh(string type) { EnvironmentSetting.Debug($"再表示へ開始:{type}"); StateHasChanged(); EnvironmentSetting.Debug($"再表示へ終了:{type}"); //await InvokeAsync(StateHasChanged); //OnRefresh?.Invoke(); } }