續接上一節,今天我們要解析的這個頁面是建立請假申請的最後一個頁面ConfirmationScreen。
-
頁面結構
這個頁面初看感覺很簡單,稍一拆析發現還是有點意思,這個頁面裡其實是包括了兩個“頁面”。一起來看:
最初從左側的樹狀視圖裡選中最上層的 ConfirmationScreen時,右側的頁面布局展示區域顯示的是下圖中間那個綠色對勾的頁面。結合頁面的名字來看,第一感覺這個頁面就是簡單的實作了對請假申請進行确認的功能。把這個頁面的第一級展開後,看到的是兩個組合:ManagerView,EmployeeView,再具體全部展開,就有意思了。最終的頁面結構見下圖:
綠對勾的頁面是EmployeeVieew視圖頁面,ManagerView是右邊标題為"Declined Request"這個頁面。
-
控件解析*
首先還是看最高層的 ConfirmationScreen,設定了OnVisible 的屬性。
代碼的總體是執行了一個IF語句//開始真是小看了這個頁面,單單這個OnVisible的代碼段長度,就超過了之前解析的其它頁面. 下面一起分析一下:
和一個Concurrent語句(根據不同狀态處理假期申請,發送郵件通知)
, 見下面精簡的代碼段。(為新申請把相關參數還原成預設值)
IF(_submittingRequest, `//如果正在送出,就繼續下面的If子語句,否則跳過`
If(_editingRequest, `//如果是正在編輯,就執行下面第一個Concurrent函數`
Concurrent(
Patch(),
If(),
ClearCollect()
),
Concurrent( `//如果不是正在編輯,就執行下面的Concurrent函數`
Patch(),
ClearCollect()
); `//子If 結束`
Forall (); `//與子If語句平級`
Office365Outlook.SendEmailV2(); `//與子If語句平級`
ForAll() `//與子If語句平級`
); `//IF 語句結束`
CONCURRENT()
再來看具體的代碼:
If(
_submittingRequest, `//主IF的條件,_submittingRequest是上一節在送出按鈕上設定的變量`
If( `//子IF 開始: _submittingRequest為真的代碼段`
_editingRequest, `//子If 語句的條件,如果是編輯狀态,就執行下面第一個Concurrent函數`
Concurrent( `//并發處理幾個任務,1.對目前正在編輯的記錄進行修改;2.如果主管準許,相應的處理已用假期天數;3.生成後續要發送的電子郵件的内容模闆`
Patch( `//Concurrent的第一個函數`
Leave, `Patch的第一個參數,資料源`
GalleryRequests.Selected, `Patch的第二個參數,要修改的表`
{
Title: First(RequestEdit).Title,
Detail: First(RequestEdit).Detail,
StartDate: First(RequestEdit).StartDate,
EndDate: First(RequestEdit).EndDate,
LeaveType: First(RequestEdit).LeaveType,
Approver: First(RequestEdit).Approver,
Status: First(RequestEdit).Status,
Requester: First(RequestEdit).Requester,
LeaveID: GalleryRequests.Selected.LeaveID
} `Patch的第三個參數,花括号裡的就是表裡各條記錄的新值`
),
If(`//Concurrent的第二個函數`
_managerView && _managerApproved, `如果主管準許請假`
Patch( `就按照員工申請的假期類型,把員工本次申請的天數加到已用的假期天數裡,
然後更新到Balance假期天數集合`
Balance,
LookUp(Balance, BalanceID = _requesterBalanceRecord.BalanceID),
If(
Upper(First(RequestEdit).LeaveType) = "VACATION",
{VacationUsed: Value(_requesterBalanceRecord.VacationUsed) + First(RequestEdit).DaysCount},
Upper(First(RequestEdit).LeaveType) = "SICK LEAVE",
{SickUsed: Value(_requesterBalanceRecord.SickUsed) + First(RequestEdit).DaysCount},
Upper(First(RequestEdit).LeaveType) = "FLOATING HOLIDAY",
{FloatingUsed: Value(_requesterBalanceRecord.FloatingUsed) + First(RequestEdit).DaysCount},
Upper(First(RequestEdit).LeaveType) = "JURY DUTY",
{JuryDutyUsed: Value(_requesterBalanceRecord.JuryDutyUsed) + First(RequestEdit).DaysCount},
Upper(First(RequestEdit).LeaveType) = "BEREAVEMENT",
{BereavementUsed: Value(_requesterBalanceRecord.BereavementUsed) + First(RequestEdit).DaysCount}
)
)
),
ClearCollect( `//Concurrent的第三個函數- 作用:從RequestEdit記錄裡擷取資料,生成郵件模闆`
TemplateData, `更新集合TemplateDate的各個值`
{Field: "{SubmitterName}",Data: If(_managerView, _requester.DisplayName, _myProfile.DisplayName)},
{Field: "{LeaveType}",Data: First(RequestEdit).LeaveType},
{Field: "{LeaveTitle}",Data: First(RequestEdit).Title},
{Field: "{LeaveDescript}",Data: First(RequestEdit).Detail},
{Field: "{LeaveStart}",Data: Text(First(RequestEdit).StartDate,"[$-en-US]mmm. dd, yyyy")},
{Field: "{LeaveEnd}",Data: Text(First(RequestEdit).EndDate,"[$-en-US]mmm. dd, yyyy")},
{Field: "{LeaveStatus}",Data: First(RequestEdit).Status}
) `ClearCollect結束`
), `//第一個Concurrent結束`
Concurrent( `子If語句對應條件 _editingRequest 為false 的運作邏輯,也就是建立申請的處理`
Patch( `第二個Concurrent函數的第一個子函數 Patch()`
Leave,
Defaults(Leave),
{
Title: AboutLeaveTitleInput.Text,
Detail: AboutLeaveDetailInput.Text,
StartDate: LeaveStartDatePicker.SelectedDate,
EndDate: LeaveEndDatePicker.SelectedDate,
LeaveType: _selectedLeaveType.type,
Approver: _defaultApprover.UserPrincipalName,
Status: "Pending",
Requester: _myProfile.UserPrincipalName,
LeaveID: Text(Now(),DateTimeFormat.LongDateTime24) & _myProfile.UserPrincipalName
}
), `Patch函數到此結束`
ClearCollect( `第二個Concurrent函數的第二個子函數 ClearCollect - 作用:從使用者輸入的字段裡擷取資料生成郵件模闆`
TemplateData, `為電子郵件模闆收集資料`
{Field: "{SubmitterName}",Data: If(_managerView, _requester.DisplayName, _myProfile.DisplayName)},
{Field: "{LeaveType}",Data: _selectedLeaveType.type},
{Field: "{LeaveTitle}", Data: AboutLeaveTitleInput.Text},
{Field: "{LeaveDescript}",Data: AboutLeaveDetailInput.Text},
{Field: "{LeaveStart}",Data: Text(LeaveStartDatePicker.SelectedDate,"[$-en-US]mmm. dd, yyyy")},
{Field: "{LeaveEnd}",Data: Text(LeaveEndDatePicker.SelectedDate,"[$-en-US]mmm. dd, yyyy" )}
) `//ClearCollect結束`
) `//第二個Concurrent結束`
); `//内嵌子IF語句結束,但這裡是分号,依舊是 _submittingRequest為真的代碼段`
`//給模闆資料設定好辨別符,主管登入使用第二行的模闆資料,否則用第一行的資料`
ForAll(
TemplateData,
Patch(
EmailTemplate,
LookUp(EmailTemplate,If(_managerView,Row = 2,Row = 1)),
{ Value: Substitute(
LookUp(EmailTemplate,If( _managerView,Row = 2,Row = 1)).Value,
Field,
Data
)
}
) `//Patch 結束`
);`//ForAll代碼段結束,但這裡是分号,接下來依舊是 _submittingRequest為真的代碼段`
Office365Outlook.SendEmailV2( `按照上面的模闆,發送電子郵件`
If(_managerView, _requester.UserPrincipalName, _defaultApprover.UserPrincipalName),
If(_managerView, "Leave Request " & First(RequestEdit).Status, "New Leave Request"),
LookUp(EmailTemplate,If(_managerView, Row = 2, Row = 1)).Value,
{Importance: "Normal"}
); `//SendEmailV2代碼段結束,但這裡是分号,接下來依舊是 _submittingRequest為真的代碼段`
ForAll( `//把郵件模闆恢複預設值`
TemplateData,
Patch(EmailTemplate,
LookUp(EmailTemplate,If(_managerView,Row = 2,Row = 1)),
{Value: Substitute(LookUp(EmailTemplate,If(_managerView,Row = 2,Row = 1)).Value,Data,Field)}
) `//Patch 結束`
) `//ForAll 結束`
); `//頂級IF代碼段結束`
Concurrent( `//為下一個新的申請把所有相關參數恢複回預設值`
Set(_submittingRequest,false),
Set(_reviewRequest, false),
Reset(AboutLeaveTitleInput),
Reset(AboutLeaveDetailInput),
Reset(LeaveStartDatePicker),
Reset(LeaveEndDatePicker),
Set(_defaultApprover,Blank()),
Set(_defaultApproverPhoto,Blank()),
Set(_selectedLeaveType,Blank()
)
)
好了,後續逐個拆解頁面下的控件就比較簡單了,先看一下兩個組控件的設定
ManagerView - 主管視圖組
預設的這個視圖是不可見的,相應的設定為: Visible = _managerView
EmployeeView - 員工視圖, 沒有特殊設定
最後,具體來看每個子控件:
2.1 BtnDone_1
-
OnSelect =
//頁面最下面的按鈕。設定了兩個動作:重置變量ChangeRequestMessageInput_1, 然後跳轉回HomeScreen
Reset(ChangeRequestMessageInput_1); Navigate(HomeScreen, None)
2.2 LblChangeRequestScreenHeader_1 - 頁面标題
-
Text =
//根據審批狀态,顯示Approved Request 或者 Declined Request
If(_managerApproved, “Approved”, “Declined”) & " Request"
2.3 Label23_1 -
-
Text =
//組合出來的一個字元串, 用到了下面的 2.5控件
"Message to " & First(Split(ChangeRequestRequster_1.Text, " “)).Result & " (optional)”
2.4 ChangeRequestResult_1 - 審批結果文本描述
-
Text =
//根據不同狀态,設定不同的審批結果描述
If(_submittingRequest, “Processing request…”, _managerApproved,“The request has been approved.”,“The request has been declined.”)
2.5 ChangeRequestRequster_1 - 申請人名稱
-
Text = _requester.DisplayName
放一個截圖在下面,可以參考一下實際的效果。
//Test62.F1就是這個這個控件的顯示結果。
2.6ChangeRequestTitle_1 - 請假申請天的假條擡頭
-
Text = First(RequestEdit).Title
//如上圖的JuryDuty,對應的就是這個控件
2.7 ChangeRequestDue_1 - 假期起止日期
-
Text =
//從RequestEdit集合裡擷取出假期的起止日,然後用Text函數轉成字元串文本
Text(First(RequestEdit).StartDate,"[KaTeX parse error: Expected 'EOF', got '&' at position 30: …mm. dd, yyyy") &̲ If(DateDiff(Fi…-en-US]dddd, mmm. dd, yyyy"))
2.8 ChangeRequestContentSeperator_1 - 區域分割線條
2.9 ChangeRequestMessageInput_1 - 文本輸入框
這個文本框用于在主管點選審批按鈕跳轉到這個頁面後,可以再輸入一些備注性的文字然後點發送按鈕來給申請人發送郵件通知,不過這個是可選的。一旦從上一個頁面點選審批按鈕跳轉到這個頁面,頁面最下的Done按鈕很快就會被啟用,來結束整個審批流程。
該文本框自身沒有運作邏輯的設定,隻是用來為下一個控件提供資料。
2.10 ChangeRequestSendButton_1 - 發送按鈕
這個按鈕預設是被禁用的(由DisplayMode控制)。
-
DisplayMode =
//RequestEdit為空,也就是說目前沒有假期申請,就禁用按鈕。否則啟用按鈕。
If(IsEmpty(RequestEdit), Disabled, DisplayMode.Edit)
2.11 icon3 - 右上角的 “X” 按鈕
-
Visible = !_submittingRequest && !_managerView
//做了可見性的條件設定
-
OnSelect = Navigate(HomeScreen, None)
//跳轉回HomeScreen
2.12 icon4 - 綠色對勾圖示
-
Visible = !_managerView
-
Icon = Icon.Check
-
OnSelect = Navigate(HomeScreen, None)
2.13 Label10 - 狀态提示文字标簽
-
Visible = !_managerView
-
Text = "Your request " & If(_submittingRequest, “is being processed…”, “has been submitted”)
本節用到了這些函數,其中有幾個還是之前沒用過的:
IF, Concurrent, Patch, First, LookUp, Upper, ClearCollect, Defaults, Text, ForAll, Substitue, Set, Reset
另外,本節還用到了調用O365郵件功能子產品來發送郵件的實作方法: Office365Outlook.SendEmailV2(), 之前某節裡應該有用到過,在這裡特别提一下,這個SendEmailV2不是PowerApps自身的函數,而是Exchange Online郵件功能子產品裡的函數,需要通過添加連接配接器來引入這個功能。具體如何實作請參考另一博文。
好了,原本想在本節最後對之前的解析做個小結,沒想到一上來就來了個大體量的内容,在最高的頁面層級做了很多運作邏輯的設定。好在接下來的子控件就沒有太多新東西了,不過通過兩個組控件在一個頁面上實作兩個子頁面倒是這個解析的亮點,可以開拓一下解決問題時的思維方式。
----先到這裡,我們下節專門對之前的解析做個小結。下一節再見。。。-----