上一节我们完成了页面 NewRequestScreen 的解析,那个页面相当于是提交请假申请的第一步,选择要请的假期的类型。
本节接下来会沿着这个申请,一步步解析相关的页面。
当点选了假期类型后,所跳转到的页面就是下面这个 SelectDatesScreen,让用户选择假期的开始日期和结束日期。
1. 页面结构
从上图能看出的是,整个页面由多种独立控件组成。从上到下,这个页面涉及到图标、标签、图片、几何形状、日期拾取器和按钮六种控件。
2. 控件解析
接下来我们就逐个看这些控件和如何结合在一起运行的。
首先,注意到SelectDatesScreen层面上设置了 OnVisible 属性:
OnVisible = If(!_editingRequest && !_reviewRequest, Set(_leaveStart,Today());Set(_leaveEnd,Today())); //如果当前既不是编辑之前的申请(!_editingRequest) 也不是查看之前的申请(!_reviewRequest),就把开始日期和结束日期都设置成当天(相当于是新建一个申请)。
//注意: 本节多次用到这两个布尔类型的变量 _editingRequest 和 _reviewRequest。
//这两个变量在之前的HomeScreen里有做过初始化设置,后续根据申请页面的状态不同,还有几个页面也对其进行了初始化设置。
2.1 iconBackSelectDates - 左上角的返回按钮
OnSelect = Back();Reset(LeaveStartDatePicker);Reset(LeaveEndDatePicker) //返回上一页,然后重置两个日期拾取器
2.2 LabelSelectDatesHeader - 页面标题
Text = If(_editingRequest, "Edit ", "Select ") & "dates" //根据当前页面的属性是编辑之前的申请还是新建申请(通过变量 _editingRequest 进行控制),显示不同的标题
2.3 ImageLeaveType - 假期类型图标
Image = If(_editingRequest, LookUp(LeaveTypeCollection, Upper(type)=Upper(First(RequestEdit).LeaveType)).icon, _selectedLeaveType.icon)
//和上面的标签控件很类似,也是根据当前页面的属性进行相应的后续处理。区别在于上面是直接固定设定好不同情况下的文字提示,这里是根据情况设置不同的图标。
//如果当前是编辑页面,就通过LookUp函数从LeaveTypeCollection里找出对应的假期类型图标; 否则就利用上一节传递过来的变量 _selectedLeaveType, 其实也是个对象,通过 . 运算符来获取所选的假期对应的图标。
//这里还涉及到第四节设置的一个集合 RequestEdit ,其中存储的时用户要编辑的请假申请数据,后续会继续提及。
2.4 LabelLeaveType - 假期类型名称
Text = If(_editingRequest, First(RequestEdit).LeaveType, _selectedLeaveType.type) //还是类似的逻辑,同时又用到了上一节传来的变量
2.5 LabelCurrentBalanceText
Text = "Current balance: " //文字固定的提示标签
2.6 LabelCurrentBalanceVal - 当前可用的假期天数
Text = LookUp(RequesterBalanceCollection, //Lookup函数的第一个参数:要去查询的数据源,这里设定的是第三节里创建的集合 RequesterBalanceCollection。顺便回忆一下,这个集合里存储的是员工当前可用的假期天数。
Upper(type)=If(_editingRequest, Upper(First(RequestEdit).LeaveType), Upper(_selectedLeaveType.type)), //Lookup 函数的第二个参数:查询条件
Text(balance) &If(balance<2, " day", " days")) //LookUp函数的结果,这里利用Text函数将依据 Type条件查询返回的结果,也就是假期天数(balance)转成了文本,然后和文字 day 或者 days组合成新的文本。
上面四个控件在有数据时,所显示出来的效果如下图所示:
2.7 Rectangle9_4 - 区域分割线
2.8 LabelSelectDates - 提示性的文本信息
Text = "Select Dates" //文字固定的提示标签
2.9 Label19 -- 假期起始日期的提示性文本
Text = "*From" //文字固定的提示标签
2.10 LeaveStartDatePicker - 用来选择假期的起始日期
这个控件有四个关键的属性设置:
- DateTimeZone = Local //日期的时区设置
- DefaultDate = If(_editingRequest, First(RequestEdit).StartDate, _reviewRequest, _leaveStart ,Today()) //设置页面打开后,日期选择器默认显示的日期。这里利用 IF(条件1,结果1,条件2,结果2,..., 条件n, 结果n, 默认结果 )的格式,定义了三种不同状态的显示日期: 编辑状态,查看状态,其它状态。
- OnChange = Set(_leaveStart, LeaveStartDatePicker.SelectedDate) //利用日期选择器选择了其它日期后,将新选的日期赋值给变量 _leaveStart
- StartOfWeek = StartOfWeek.Sunday //每周的起始日期设置为周日,这个设置会直接影响到诸如第四节里的假期天数计算逻辑。
2.11 Label19_1 - 假期结束日期的提示性文本
Text = "*From" //文字固定的提示标签
2.12 LeaveEndDatePicker - 用来选择假期的结束日期
运行逻辑和上面那个选择起始日期的控件一样,只是从开始日期变成了结束日期。
- DateTimeZone = Local
- DefaultDate = If(_editingRequest, First(RequestEdit).EndDate, _reviewRequest, _leaveEnd, Today())
- OnChange = Set(_leaveEnd, LeaveEndDatePicker.SelectedDate)
- StartOfWeek = StartOfWeek.Sunday
2.13 Label1 - 当日期选择不规范时(比如结束日期比开始日期还早),用来显示警告信息,提示用户重新选择正确的日期。
Text = If(LeaveStartDatePicker.SelectedDate > LeaveEndDatePicker.SelectedDate || And(!_editingRequest, LeaveStartDatePicker.SelectedDate < Today()), "Start date must occur on or " & If(LeaveStartDatePicker.SelectedDate > LeaveEndDatePicker.SelectedDate,"before end date", "after today"))
如下图,出错时的警告提示效果:
2.14 Rectangle9_5 - 区域分割线
2.15 ButtonNextSelectDates - 页面最下面的下一步按钮
OnSelect =
//**** 计算假期申请天数的预备代码段 - 开始位置 ****和第四节里计算假期申请天数的代码段相同的逻辑
If(LeaveStartDatePicker.SelectedDate <= LeaveEndDatePicker.SelectedDate, //只是这里开头是通过一个 IF语句来处理条件判断,第四节里是直接开始
Set(_inclusiveTotalDaysRequested, DateDiff(LeaveStartDatePicker.SelectedDate, LeaveEndDatePicker.SelectedDate, Days) + 1); //这里也有点不同,但也只是变量名称的差别,逻辑上完全一样。
Set(_numFullWeeks, RoundDown(_inclusiveTotalDaysRequested / 7, 0));
Set(_numFullDaysPartialWeek, _inclusiveTotalDaysRequested - _numFullWeeks * 7);
Concurrent(Set(_startWeekday, Weekday(LeaveStartDatePicker.SelectedDate)), Set(_endWeekday, Weekday(LeaveEndDatePicker.SelectedDate)));
//下面的IF 代码段计算从所申请的总天数减去整周用掉的天数后,剩下的工作日天数
If(_numFullDaysPartialWeek = 6, If(_startWeekday <= 2, Set(_numPartialWeekdays, 5), Set(_numPartialWeekdays, 4) ),
_numFullDaysPartialWeek = 5, If(_startWeekday = 2, Set(_numPartialWeekdays, 5), _startWeekday = 1 || _startWeekday = 3 || _startWeekday = 4, Set(_numPartialWeekdays, 4), Set(_numPartialWeekdays, 3) ),
_numFullDaysPartialWeek = 4, If(_startWeekday = 2 || _startWeekday = 3, Set(_numPartialWeekdays, 4), _startWeekday = 1 || _startWeekday = 4, Set(_numPartialWeekdays, 3), Set(_numPartialWeekdays, 2) ),
_numFullDaysPartialWeek = 3, If(_startWeekday = 6 || _startWeekday = 7, Set(_numPartialWeekdays, 1), _startWeekday = 1 || _startWeekday = 5, Set(_numPartialWeekdays, 2), Set(_numPartialWeekdays, 3) ),
_numFullDaysPartialWeek = 2, If(_startWeekday = 7, Set(_numPartialWeekdays, 0), _startWeekday = 1 || _startWeekday = 6, Set(_numPartialWeekdays, 1), Set(_numPartialWeekdays, 2)),
_numFullDaysPartialWeek = 1, If(_startWeekday = 1 || _startWeekday = 7, Set(_numPartialWeekdays, 0), Set(_numPartialWeekdays, 1) ),
_numFullDaysPartialWeek = 0, Set(_numPartialWeekdays, 0)
);
Set(_workDaysInRequest, _numFullWeeks * 5 + _numPartialWeekdays);
Set(_holidaysInRequest, CountIf(Holidays, StartDate >= LeaveStartDatePicker.SelectedDate, StartDate <= LeaveEndDatePicker.SelectedDate));
Set(_requestedDays, _workDaysInRequest - _holidaysInRequest)
);
//**** 计算假期申请天数的代码段 - 结束位置 - 最终请假的天数存储在上面最后一个变量 _requestedDays 里****
If(_editingRequest, Patch(RequestEdit, First(RequestEdit), {
StartDate: LeaveStartDatePicker.SelectedDate, EndDate: LeaveEndDatePicker.SelectedDate, DaysCount:_requestedDays }) ; Navigate(EditRequestScreen, None),
_reviewRequest, Back(),
Navigate(SelectApproverScreen, None)
)
//最后这个 IF 是在上面计算完假期天数后,再根据不同的页面状态执行不同的动作。
//如果当前是编辑页面,就把新选的日期通过 Patch 命令暂存到 RequestEdit 集合里,然后跳转到EditRequestScreen页面;
//如果当前是查看页面,就直接返回上一个页面;
//其它状态,就跳转到SelectApproverScreen页面。
附注:本节用到的函数:
Set, Concurrent, IF, Navigate, Reset, LookUp, First, Upper, Text, Patch
----------- 本节完成了对SelectDatesScreen页面的解析,我们下一节再见------------