From 094468b0eb0ba1c0e5e42f0dcd630a5144c38bfd Mon Sep 17 00:00:00 2001 From: tianshujie Date: Mon, 13 Nov 2023 17:13:52 +0800 Subject: [PATCH] * Refactor activate a task page. --- module/task/config/form.php | 2 +- module/task/js/activate.ui.js | 368 ++++++++++++++++--------------- module/task/ui/activate.html.php | 122 +++++----- module/task/zen.php | 2 +- 4 files changed, 265 insertions(+), 229 deletions(-) diff --git a/module/task/config/form.php b/module/task/config/form.php index 532096ad93..b8ed6e0a82 100644 --- a/module/task/config/form.php +++ b/module/task/config/form.php @@ -156,7 +156,7 @@ $config->task->form->start['lastEditedBy'] = array('type' => 'string', 'requ $config->task->form->start['lastEditedDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); $config->task->form->finish = array(); -$config->task->form->finish['realStarted'] = array('type' => 'date', 'required' => true); +$config->task->form->finish['realStarted'] = array('type' => 'datetime', 'required' => true); $config->task->form->finish['left'] = array('type' => 'float', 'required' => false, 'default' => 0); $config->task->form->finish['consumed'] = array('type' => 'float', 'required' => false, 'default' => 0); $config->task->form->finish['assignedTo'] = array('type' => 'string', 'required' => false, 'default' => ''); diff --git a/module/task/js/activate.ui.js b/module/task/js/activate.ui.js index 88d939ac18..581cb1aba6 100644 --- a/module/task/js/activate.ui.js +++ b/module/task/js/activate.ui.js @@ -1,166 +1,34 @@ -$(function() +$(document).on('#left', 'input', setTeamUser); + +window.manageTeam = function() { - $('.team-group, [data-dismiss="modal"]').on('click', function() + const showTeam = $('#multiple').is(":checked"); + + let $assignedToPicker = $('[name=assignedTo]').zui('picker'); + $assignedToPicker.$.setValue(''); + $assignedToPicker.render({items: teamItems, disabled: showTeam}); + + $('.multi-append').empty(); + $('.member-done').find('input[name^=teamLeft]').val(0); + + $('#left').val(''); + if(showTeam) { - $('#modalTeam').show(); - }); - - $('#confirmButton').on('click', function() - { - let memberCount = ''; - let totalEstimate = 0; - let totalConsumed = oldConsumed; - let totalLeft = 0; - let error = false; - $('select[name^=team]').each(function() - { - if($(this).val() == '') return; - - memberCount++; - - let $tr = $(this).closest('tr'); - let account = $(this).val(); - let realname = members[account]; - - let estimate = parseFloat($tr.find('[name^=teamEstimate]').val()); - if(!isNaN(estimate)) totalEstimate += estimate; - if($tr.hasClass('member-wait') && (isNaN(estimate) || estimate <= 0)) - { - zui.Modal.alert(realname + ' ' + estimateNotEmpty); - error = true; - return false; - } - - let consumed = parseFloat($tr.find('[name^=teamConsumed]').val()); - if(!isNaN(consumed)) totalConsumed += consumed; - - let $left = $tr.find('[name^=teamLeft]'); - let left = parseFloat($left.val()); - if(!isNaN(left)) totalLeft += left; - if(!$left.prop('readonly') && $tr.hasClass('member-wait') && (isNaN(left) || left <= 0)) - { - zui.Modal.alert(realname + ' ' + leftNotEmpty); - error = true; - return false; - } - }) - - if(error) return false; - - if(memberCount < 2) - { - zui.Modal.alert(teamMemberError); - return false; - } - - $('#left').val(totalLeft); - updateAssignedTo(); - - $('#modalTeam').hide(); - }); - - $('input[name=multiple]').on('click', function() - { - let showTeam = $(this).is(":checked"); - $('#assignedTo').val(''); - $('#left').val(''); - $('#dataPlanGroup').removeClass('required'); - $('.multi-append').empty(); - $('.teamTemplate select, .teamTemplate input').val(''); - $('.teamTemplate input[name^=teamConsumed]').val('0'); - - /* Reset team modal. */ - let oldTeamCount = $('#taskTeamEditor select:disabled').length; - let removeCount = oldTeamCount > 6 ? 1 : (5 - oldTeamCount); - $('#taskTeamEditor tr.teamTemplate').filter(':gt(' + removeCount + ')').remove(); - $('.member-done').find('input[name^=teamLeft]').val(0); - - /* Reset assignedTo select.*/ - $('#assignedTo').replaceWith(assignedToHtml); - - if(showTeam) - { - $('#assignedTo').attr('disabled', true); - $('#left').attr('readonly', true); - $('.team-group').removeClass('hidden'); - } - else - { - $('.team-group').addClass('hidden'); - $('#assignedTo').removeAttr('disabled'); - $('#left').removeAttr('readonly'); - $('#dataPlanGroup').addClass('required'); - } - }); - - $('#assignedTo').on('change', setTeamUser); - $('#left').on('input', setTeamUser); - - $('#submit').on('click', function() - { - if(isMultiple) - { - let multiple = $('#multiple').is(":checked"); - if(!multiple) - { - let assignedTo = $('#assignedTo').val(); - if(!assignedTo) - { - zui.Modal.alert(teamNotEmpty); - return false; - } - } - - let estimate = parseInt($('#left').val()); - if(isNaN(estimate) || estimate <= 0) - { - zui.Modal.alert(multiple ? teamLeftEmpty : leftNotEmpty); - return false; - } - - $('#assignedTo').val(''); - } - }); -}); - -/* Update assignedTo. */ -function updateAssignedTo() -{ - let html = ''; - let multiple = $('#multiple').prop('checked'); - let assignedTo = $('#assignedTo').val(); - if(multiple) - { - let isTeamMember = false; - let mode = $('#mode').val(); - $('select[name^=team]').each(function() - { - if(!$(this).val()) return; - if($(this).val() == currentUser) isTeamMember = true; - - let account = $(this).val(); - let realName = members[account]; - let selected = account == assignedTo ? 'selected' : ''; - - html += ""; - }); - $('[name=assignedTo]').attr('disabled', 'disabled'); + $('#left').attr('readonly', true); + $('.team-group').removeClass('hidden'); } else { - for(key in members) - { - let selected = key == assignedTo ? 'selected' : ''; - html += ""; - } + $('#left').removeAttr('readonly'); + $('.team-group').addClass('hidden'); } - $('#assignedTo').html(html); - if(multiple && mode == 'linear' && $('#modalTeam tr.member-doing').length == 0 && $('#modalTeam tr.member-wait').length >= 1) $('[name=assignedTo]').val($('#modalTeam tr.member-wait:first').find('select[name^=team]:first').val()); + if(taskMode == 'multi') disableMembers(); } /** - * Set team user form. + * 设置团队成员。 + * Set team members. * * @access public * @return void @@ -169,15 +37,16 @@ function setTeamUser() { $('.multi-append').empty(); - let assignedTo = $('#assignedTo').val(); - let estimate = parseInt($('#left').val()); + const assignedTo = $('[name=assignedTo]').val(); + const estimate = parseInt($('#left').val()); + if(!assignedTo || !estimate) return; if(taskMode == 'multi') { $('.member-done').each(function() { - if($(this).find('select').val() == assignedTo) $(this).find('input[name^=teamLeft]').val(estimate); + if($(this).find('[name^team]').val() == assignedTo) $(this).find('input[name^=teamLeft]').val(estimate); }); } else @@ -191,19 +60,167 @@ function setTeamUser() } } +/** + * 检查团队成员数量。 + * Check the number of team members. + * + * @access public + * @return void + */ +function checkTeam() +{ + let memberCount = ''; + let totalEstimate = 0; + let totalConsumed = oldConsumed; + let totalLeft = 0; + let error = false; + + $('.team-select [name^=team]').each(function() + { + if($(this).val() == '') return; + + memberCount++; + + let $tr = $(this).closest('tr'); + let account = $tr.find('.picker-single-selection').text(); + let estimate = parseFloat($tr.find('[name^=teamEstimate]').val()); + + if(!isNaN(estimate)) totalEstimate += estimate; + + if($tr.hasClass('member-wait') && (isNaN(estimate) || estimate <= 0)) + { + zui.Modal.alert(account + ' ' + estimateNotEmpty); + error = true; + return false; + } + + let consumed = parseFloat($tr.find('[name^=teamConsumed]').val()); + if(!isNaN(consumed)) totalConsumed += consumed; + + let $left = $tr.find('[name^=teamLeft]'); + let left = parseFloat($left.val()); + if(!isNaN(left)) totalLeft += left; + if(!$left.prop('readonly') && $tr.hasClass('member-wait') && (isNaN(left) || left <= 0)) + { + zui.Modal.alert(account + ' ' + leftNotEmpty); + error = true; + return false; + } + }); + + if(error) return false; + + if(memberCount < 2) + { + zui.Modal.alert(teamMemberError); + return false; + } + + $('#left').val(totalLeft); + + updateAssignedTo(); + zui.Modal.hide(); +} + + +/** + * 更新指派人。 + * Update assignedTo. + * + * @access public + * @return void + */ +function updateAssignedTo() +{ + const multiple = $('#multiple').is(":checked"); + let assignedTo = $('[name=assignedTo]').val(); + let $assignedToPicker = $('[name=assignedTo]').zui('picker'); + if(multiple) + { + let isTeamMember = false; + let mode = $('#mode').val(); + let assignedToItems = new Array(); + let index = 0; + + $('.team-select [name^=team]').each(function() + { + let $tr = $(this).closest('tr'); + let realName = $tr.find('.picker-single-selection').text(); + if(realName == '') return; + + let account = $(this).val(); + + assignedToItems[index] = {'value': account, 'text': realName}; + index ++; + }); + + if(multiple && mode == 'linear' && $('#modalTeam tr.member-doing').length == 0 && $('#modalTeam tr.member-wait').length >= 1) assignedTo = assignedToItems[0].value; + + $assignedToPicker.render({items: assignedToItems, disabled: true}); + } + else + { + $assignedToPicker.render({items: memberItems}); + } + $assignedToPicker.$.setValue(assignedTo); +} -/* Mange team. */ $('#teamForm').on('click.team', '.btn-add', function() { + /* Copy row and set value is empty. */ let $newRow = $(this).closest('tr').clone(); $newRow.find('td.required').removeClass('required'); - $newRow.find('select,input').val(''); + $newRow.find('input').val(''); + + /* Get the maximum index for the team. */ + let index = 0; + let options = zui.Picker.query("[name^='team']").options; + $(".team-select").each(function() + { + let id = $(this).attr('id').substring(4); + + id = parseInt(id); + id ++; + + index = id > index ? id : index; + }); + + /* Reset the team dropdown container. */ + $newRow.find('.team-select.picker-box').remove(); + $newRow.find('td').eq(1).append('
'); + + /* Add a new row. */ $(this).closest('tr').after($newRow); + /* Init team's select picker. */ + options.defaultValue = ''; + options.disabled = false; + + let members = []; + let $teams = $('#teamForm').find('.team-select [name^=team]'); + for(i = 0; i < $teams.length; i++) + { + let value = $teams.eq(i).val(); + if(members.includes(value)) + { + $teams.eq(i).closest('tr').addClass('hidden'); + continue; + } + if(value != '') members.push(value); + } + + $.each(options.items, function(i, item) + { + if(item.value == '') return; + options.items[i].disabled = members.includes(item.value); + }); + new zui.Picker(`#team${index}`, options); + + /* Process releate data. */ toggleBtn(); setLineIndex(); - disableMembers(); -}) +}); + $('#teamForm').on('click.team', '.btn-delete', function() { @@ -216,6 +233,7 @@ $('#teamForm').on('click.team', '.btn-delete', function() setLineIndex(); }); + /** * Set line number. * @@ -230,7 +248,6 @@ function setLineIndex() $(this).text(index); index ++; }); - } /** @@ -264,7 +281,7 @@ function disableMembers() if(mode == 'multi') { let members = []; - let $teams = $('#teamForm').find('select[name^=team]'); + let $teams = $('#teamForm').find('.team-select [name^=team]'); for(i = 0; i < $teams.length; i++) { let value = $teams.eq(i).val(); @@ -278,19 +295,24 @@ function disableMembers() $teams.each(function() { - let $this = $(this); - let value = $this.val(); - $this.find('option:disabled').removeAttr('disabled'); - $.each(members, function(i, account) + let $team = $(this); + let account = $team.val(); + let $teamPicker = $team.zui('picker'); + let teamItems = $teamPicker.options.items; + $.each(teamItems, function(i, item) { - if(account == value) return; - $this.find('option[value=' + account + ']').attr('disabled', 'disabled'); + if(item.value == '') return; + teamItems[i].disabled = members.includes(item.value) && item.value != account; }) + + $teamPicker.render({items: teamItems}); }); + $('#teamForm').find('tr.hidden').remove(); } } + /** * Check if it can be removed. * @@ -300,7 +322,7 @@ function disableMembers() */ function checkRemove(removeIndex) { - let $teams = $('#teamForm').find('select#team'); + let $teams = $('#teamForm').find('.team-select [name^=team]'); let totalLeft = 0; let memberCount = 0; for(i = 0; i < $teams.length; i++) @@ -333,7 +355,8 @@ function checkRemove(removeIndex) return true; } -$('#teamForm').on('change', 'select[name^=team]', function() + +$('#teamForm').on('change', '.team-select [name^=team]', function() { $(this).closest('tr').find('input[name^=teamLeft]').closest('td').toggleClass('required', $(this).val() != '') @@ -353,8 +376,3 @@ $('#teamForm').on('change', 'select[name^=team]', function() $tr.find('[name^=teamConsumed]').val(consumed); $tr.find('[name^=teamEstimate]').val(estimate); }); - -$('#teamForm').find('select#team:enabled').each(function() -{ - $(this).closest('tr').find('input[name^=teamLeft]').closest('td').toggleClass('required', $(this).val() != '') -}); diff --git a/module/task/ui/activate.html.php b/module/task/ui/activate.html.php index 4a372f6edd..85edafbf1a 100644 --- a/module/task/ui/activate.html.php +++ b/module/task/ui/activate.html.php @@ -12,7 +12,6 @@ namespace zin; /* ====== Preparing and processing page data ====== */ jsVar('oldConsumed', $task->consumed); jsVar('currentUser', $app->user->account); -jsVar('members', $members); jsVar('teamMemberError', $lang->task->error->teamMember); jsVar('teamLeftEmpty', $lang->task->error->teamLeftEmpty); jsVar('totalLeftError', sprintf($this->lang->task->error->leftEmptyAB, $this->lang->task->statusList[$task->status])); @@ -21,7 +20,34 @@ jsVar('leftNotEmpty', sprintf($lang->task->error->notempty, $lang->task->left)); jsVar('teamNotEmpty', sprintf($lang->error->notempty, $lang->task->assignedTo)); jsVar('isMultiple', $isMultiple); jsVar('taskMode', $task->mode); -if($isMultiple) jsVar('assignedToHtml', html::select('assignedTo', $teamMembers, '', "class='form-control' disabled")); + +$teamData = array(); +$teamUsers = array(); +if($isMultiple) +{ + $index = 1; + foreach($task->team as $member) + { + $member->id = $index; + $member->team = $member->account; + $member->teamSource = $member->account; + $member->teamEstimate = $member->estimate; + $member->teamConsumed = $member->consumed; + $member->teamLeft = $member->left; + + $teamData[] = $member; + $teamUsers[] = $member->account; + $index ++; + } + + $teamItems = array(); + foreach($teamMembers as $key => $value) $teamItems[] = array('text' => $value, 'value' => $key); + jsVar('teamItems', $teamItems); +} + +$memberItems = array(); +foreach($members as $key => $value) $memberItems[] = array('text' => $value, 'value' => $key); +jsVar('memberItems', $memberItems); /* zin: Set variables to define control for form. */ modalHeader(); @@ -41,8 +67,8 @@ if($isMultiple) set::className('hidden'), set::name('mode'), set::value($task->mode), - ), - ), + ) + ) ); } @@ -58,6 +84,7 @@ if($isMultiple) set::name('multiple'), set::text($lang->task->manageTeam), set::rootClass('ml-4'), + on::click('manageTeam') ) ); } @@ -66,14 +93,14 @@ $leftBox = ''; if($task->parent != '-1') { $leftBox = formGroup( - set::width('1/3'), + set::width('1/2'), set::label($lang->task->left), set::name('left'), inputControl ( to::suffix($lang->task->suffixHour), set::suffixWidth(20), - ), + ) ); } @@ -84,28 +111,10 @@ if($isMultiple) set::text($lang->task->team), set::className('team-group hidden'), set::url('#modalTeam'), - set('data-toggle', 'modal'), + set('data-toggle', 'modal') ); } -$teamData = array(); -if($isMultiple) -{ - $index = 1; - foreach($task->team as $member) - { - $member->id = $index; - $member->team = $member->account; - $member->teamSource = $member->account; - $member->teamEstimate = $member->estimate; - $member->teamConsumed = $member->consumed; - $member->teamLeft = $member->left; - - $teamData[] = $member; - $index ++; - } -} - if(!empty($task->team)) $rowCount = count($task->team) < 6 ? 6 : 1 + count($task->team); $teamForm = array(); $i = 1; @@ -137,12 +146,15 @@ foreach($task->team as $member) h::td ( set::width('240px'), - select + picker ( + setID("team$i"), + setClass('team-select'), set::name('team[]'), set::value($member->account), set::items($members), set::placeholder($lang->task->assignedTo), + set::disabled($memberDisabled) ), input ( @@ -168,8 +180,8 @@ foreach($task->team as $member) set::readonly($hourDisabled), ), to::suffix($lang->task->suffixHour), - set::suffixWidth(20), - ), + set::suffixWidth(20) + ) ), h::td ( @@ -180,11 +192,11 @@ foreach($task->team as $member) set::name('teamConsumed[]'), set::value((float)$member->consumed), set::placeholder($lang->task->consumed), - set::readonly($hourDisabled), + set::readonly($hourDisabled) ), to::suffix($lang->task->suffixHour), - set::suffixWidth(20), - ), + set::suffixWidth(20) + ) ), h::td ( @@ -196,11 +208,11 @@ foreach($task->team as $member) set::name('teamLeft[]'), set::value((float)$member->left), set::placeholder($lang->task->left), - set::readonly($hourDisabled), + set::readonly($hourDisabled) ), to::suffix($lang->task->suffixHour), - set::suffixWidth(20), - ), + set::suffixWidth(20) + ) ), h::td ( @@ -236,17 +248,19 @@ for($i; $i <= $rowCount; $i ++) h::td ( set::width('240px'), - select + picker ( + setID("team$i"), + setClass('team-select'), set::name('team[]'), set::items($members), - set::placeholder($lang->task->assignedTo), + set::placeholder($lang->task->assignedTo) ), input ( set::type('hidden'), set::name('teamSource[]'), - set::value(''), + set::value('') ) ), h::td @@ -256,10 +270,10 @@ for($i; $i <= $rowCount; $i ++) input ( set::name('teamEstimate[]'), - set::placeholder($lang->task->estimateAB), + set::placeholder($lang->task->estimateAB) ), to::suffix($lang->task->suffixHour), - set::suffixWidth(20), + set::suffixWidth(20) ), ), h::td @@ -269,10 +283,10 @@ for($i; $i <= $rowCount; $i ++) input ( set::name('teamConsumed[]'), - set::placeholder($lang->task->consumed), + set::placeholder($lang->task->consumed) ), to::suffix($lang->task->suffixHour), - set::suffixWidth(20), + set::suffixWidth(20) ), ), h::td @@ -282,10 +296,10 @@ for($i; $i <= $rowCount; $i ++) input ( set::name('teamLeft[]'), - set::placeholder($lang->task->left), + set::placeholder($lang->task->left) ), to::suffix($lang->task->suffixHour), - set::suffixWidth(20), + set::suffixWidth(20) ), ), h::td @@ -296,7 +310,7 @@ for($i; $i <= $rowCount; $i ++) ( set::items(array( array('icon' => 'plus', 'class' => 'btn ghost btn-add text-gray'), - array('icon' => 'trash', 'class' => 'btn ghost btn-delete text-gray'), + array('icon' => 'trash', 'class' => 'btn ghost btn-delete text-gray') )) ) ) @@ -311,21 +325,23 @@ formPanel ( formGroup ( - set::width('1/3'), + set::width('1/2'), set::label($lang->task->assignedTo), set::required($isMultiple), inputGroup ( - select + picker ( + setID('assignedTo'), set::name('assignedTo'), set::items($isMultiple ? $teamMembers : $members), set::value($isMultiple ? '' : $task->finishedBy), + on::change('setTeamUser') ), - $modalTeamBtn, - ), + $modalTeamBtn + ) ), - $manageTeamBox, + $manageTeamBox ), $leftBox, formGroup @@ -334,7 +350,7 @@ formPanel editor ( set::name('comment'), - set::rows('5'), + set::rows('5') ) ), modalTrigger @@ -347,11 +363,13 @@ formPanel set('data-backdrop', false), to::footer ( + div(setClass('multi-append')), btn ( setClass('primary btn-wide'), set::id('confirmButton'), set::text($lang->confirm), + on::click('checkTeam') ) ), h::table @@ -359,9 +377,9 @@ formPanel setClass('table table-form'), set::id('teamForm'), $teamForm - ), + ) ) - ), + ) ); hr(); history(); diff --git a/module/task/zen.php b/module/task/zen.php index a12dc0437b..4a7c86f01b 100644 --- a/module/task/zen.php +++ b/module/task/zen.php @@ -766,7 +766,7 @@ class taskZen extends task ->get(); if(!$this->post->currentConsumed && $oldTask->consumed == '0') dao::$errors['currentConsumed'][] = $this->lang->task->error->consumedEmpty; - if($task->realStarted > $task->finishedDate) dao::$errors['realStarted'][] = $this->lang->task->error->finishedDateSmall; + if($task->realStarted > $task->finishedDate) dao::$errors['finishedDate'][] = $this->lang->task->error->finishedDateSmall; $task->consumed = $oldTask->consumed + (float)$this->post->currentConsumed; return $task;