diff --git a/module/task/config/form.php b/module/task/config/form.php index d1b3aa73e4..90e991f1be 100644 --- a/module/task/config/form.php +++ b/module/task/config/form.php @@ -107,7 +107,7 @@ $config->task->form->batchCreate['name'] = array('type' => 'array', 're $config->task->form->batchCreate['color'] = array('type' => 'array', 'required' => false, 'default' => array()); $config->task->form->batchCreate['type'] = array('type' => 'array', 'required' => false, 'default' => array()); $config->task->form->batchCreate['assignedTo'] = array('type' => 'array', 'required' => false, 'default' => array()); -$config->task->form->batchCreate['estimated'] = array('type' => 'array', 'required' => false, 'default' => array()); +$config->task->form->batchCreate['estimate'] = array('type' => 'array', 'required' => false, 'default' => array()); $config->task->form->batchCreate['estStarted'] = array('type' => 'array', 'required' => false, 'default' => array()); $config->task->form->batchCreate['deadline'] = array('type' => 'array', 'required' => false, 'default' => array()); $config->task->form->batchCreate['desc'] = array('type' => 'array', 'required' => false, 'default' => array()); diff --git a/module/task/control.php b/module/task/control.php index 5e9d575f3b..5c8f7b815f 100755 --- a/module/task/control.php +++ b/module/task/control.php @@ -122,8 +122,10 @@ class task extends control if(!empty($_POST)) { /* 批量创建任务。 Batch create tasks. */ - $postData = form::data($this->config->task->form->batchCreate); - $tasks = $this->taskZen->prepareTasks4BatchCreate($execution, $postData->rawdata); + $postData = form::data($this->config->task->form->batchCreate); + $tasks = $this->taskZen->prepareTasks4BatchCreate($execution, $postData->get(), $taskID); + if(dao::isError()) return print(js::error(dao::getError())); + $taskIdList = $this->task->batchCreate($execution, $tasks, $taskID, $output); if(dao::isError()) return print(js::error(dao::getError())); diff --git a/module/task/js/batchcreate.ui.js b/module/task/js/batchcreate.ui.js new file mode 100644 index 0000000000..56bef6a0c7 --- /dev/null +++ b/module/task/js/batchcreate.ui.js @@ -0,0 +1,27 @@ +/* Get select of stories.*/ +function setStories() +{ + const moduleID = $(this).val(); + const index = $(this).closest('td').attr('data-init'); + const link = $.createLink('story', 'ajaxGetExecutionStories', 'executionID=' + executionID + '&productID=0&branch=all&moduleID=' + moduleID + '&storyID=0&num=' + index + '&type=short'); + $.get(link, function(stories) + { + var $story = $('#story_' + index); + var storyID = $story.val(); + if(!stories) stories = ''; + $story.replaceWith(stories); + $story.val(storyID); + if($('#zeroTaskStory').hasClass('checked')) + { + $story.find('option').each(function() + { + value = $(this).attr('value'); + if(value != 'ditto' && storyTasks[value] > 0) + { + $(this).hide(); + if(storyID == value) $story.val(''); + } + }) + } + }); +} diff --git a/module/task/js/create.ui.js b/module/task/js/create.ui.js index a9af6422b3..54c4f735c8 100644 --- a/module/task/js/create.ui.js +++ b/module/task/js/create.ui.js @@ -1,8 +1,5 @@ $(function() { - lifetimeList = JSON.parse(lifetimeList); - attributeList = JSON.parse(attributeList); - if(hideStory) { $("input[name='after'][value='toStoryList']").parent().hide(); @@ -93,6 +90,7 @@ function toggleSelectTestStory() $('#estStarted').closest('.form-row').addClass('hidden'); $('#estimate').parent().prev().addClass('hidden'); $('#estimate').parent().addClass('hidden'); + $('#testStoryBox').removeClass('hidden'); $('[name^=multiple]').prop('checked', false); showTeamBox(); @@ -105,6 +103,7 @@ function toggleSelectTestStory() $('#estStarted').closest('.form-row').removeClass('hidden'); $('#estimate').parent().prev().removeClass('hidden'); $('#estimate').parent().removeClass('hidden'); + $('#testStoryBox').addClass('hidden'); } } diff --git a/module/task/model.php b/module/task/model.php index a3014f01cf..6e1409b71c 100755 --- a/module/task/model.php +++ b/module/task/model.php @@ -1711,12 +1711,14 @@ class taskModel extends model ->andWhere('t1.vision')->eq($this->config->vision) ->fetch(); if(!$task) return false; - $task->openedDate = !empty($task->openedDate) ? substr($task->openedDate, 0, 19) : null; - $task->finishedDate = !empty($task->finishedDate) ? substr($task->finishedDate, 0, 19) : null; - $task->canceledDate = !empty($task->canceledDate) ? substr($task->canceledDate, 0, 19) : null; - $task->closedDate = !empty($task->closedDate) ? substr($task->closedDate, 0, 19) : null; + + $task->openedDate = !empty($task->openedDate) ? substr($task->openedDate, 0, 19) : null; + $task->finishedDate = !empty($task->finishedDate) ? substr($task->finishedDate, 0, 19) : null; + $task->canceledDate = !empty($task->canceledDate) ? substr($task->canceledDate, 0, 19) : null; + $task->closedDate = !empty($task->closedDate) ? substr($task->closedDate, 0, 19) : null; $task->lastEditedDate = !empty($task->lastEditedDate) ? substr($task->lastEditedDate, 0, 19) : null; - $task->realStarted = !empty($task->realStarted) ? substr($task->realStarted, 0, 19) : null; + $task->realStarted = !empty($task->realStarted) ? substr($task->realStarted, 0, 19) : null; + $task->mailto = empty($task->mailto) ? '' : $task->mailto; $children = $this->dao->select('*')->from(TABLE_TASK)->where('parent')->eq($taskID)->andWhere('deleted')->eq(0)->fetchAll('id'); $task->children = $children; diff --git a/module/task/ui/batchcreate.html.php b/module/task/ui/batchcreate.html.php new file mode 100644 index 0000000000..e9e9b694b2 --- /dev/null +++ b/module/task/ui/batchcreate.html.php @@ -0,0 +1,165 @@ + + * @package task + * @link https://www.zentao.net + */ +namespace zin; +/* ====== Preparing and processing page data ====== */ +jsVar('executionID', $execution->id); +jsVar('storyTasks', $storyTasks); + +/* zin: Set variables to define picker options for form. */ + +$items = array(); + +/* Field of ID. */ +$items[] = array +( + 'name' => 'id', + 'label' => $lang->idAB, + 'control' => 'index', + 'width' => '30px', +); + +/* Field of module. */ +$items[] = array +( + 'name' => 'module', + 'label' => $lang->task->module, + 'control' => 'select', + 'value' => $moduleID, + 'items' => $modules, + 'width' => '180px', +); + +/* Field of story. */ +if(!$hideStory) +{ + $items[] = array + ( + 'name' => 'story', + 'label' => $lang->task->story, + 'control' => 'select', + 'items' => $stories, + 'width' => '200px', + 'ditto' => true, + ); +} + +/* Field of name. */ +$items[] = array +( + 'name' => 'name', + 'label' => $lang->task->name, + 'width' => '165px', +); + +/* Field of region and lane. */ +if($execution->type == 'kanban') +{ + $items[] = array + ( + 'name' => 'regions', + 'label' => $lang->kanbancard->region, + 'control' => 'select', + 'value' => $regionID, + 'items' => $regionPairs, + 'width' => '180px', + ); + + $items[] = array + ( + 'name' => 'lanes', + 'label' => $lang->kanbancard->lane, + 'control' => 'select', + 'value' => $laneID, + 'items' => $lanePairs, + 'width' => '180px', + ); +} + +/* Field of type. */ +$items[] = array +( + 'name' => 'type', + 'label' => $lang->task->type, + 'control' => 'select', + 'items' => $lang->task->typeList, + 'width' => '100px', + 'ditto' => true, +); + +/* Field of assignedTo. */ +$items[] = array +( + 'name' => 'assignedTo', + 'label' => $lang->task->assignedTo, + 'control' => 'select', + 'items' => $members, + 'width' => '130px', + 'ditto' => true, +); + +/* Field of estimate. */ +$items[] = array +( + 'name' => 'estimate', + 'label' => $lang->task->estimateAB, + 'width' => '70px', +); + +/* Field of estStarted. */ +$items[] = array +( + 'name' => 'estStarted', + 'label' => $lang->task->estStarted, + 'control' => 'date', + 'width' => '120px', + 'ditto' => true, +); + +/* Field of deadline. */ +$items[] = array +( + 'name' => 'deadline', + 'label' => $lang->task->deadline, + 'control' => 'date', + 'width' => '120px', + 'ditto' => true, +); + +/* Field of desc. */ +$items[] = array +( + 'name' => 'desc', + 'label' => $lang->task->desc, + 'control' => 'editor', + 'width' => '150px', +); + +/* Field of pri. */ +$items[] = array +( + 'name' => 'pri', + 'label' => $lang->task->pri, + 'control' => 'select', + 'items' => $lang->task->priList, + 'width' => '130px', + 'ditto' => true, +); + +/* ====== Define the page structure with zin widgets ====== */ + +formBatchPanel +( + set::items($items), + on::change('[data-name="module"]', 'setStories'), +); + +/* ====== Render page ====== */ +render(); diff --git a/module/task/ui/create.html.php b/module/task/ui/create.html.php index a46faeb79f..b194231dd5 100644 --- a/module/task/ui/create.html.php +++ b/module/task/ui/create.html.php @@ -25,8 +25,8 @@ jsVar('requiredFields', $config->task->create->requiredFields); jsVar('estimateNotEmpty', sprintf($lang->error->gt, $lang->task->estimate, '0')); jsVar('window.lifetime', $execution->lifetime); jsVar('window.attribute', $execution->attribute); -jsVar('window.lifetimeList', json_encode($lifetimeList)); -jsVar('window.attributeList', json_encode($attributeList)); +jsVar('window.lifetimeList', $lifetimeList); +jsVar('window.attributeList', $attributeList); jsVar('hasProduct', $execution->hasProduct); jsVar('hideStory', $hideStory); $requiredFields = array(); @@ -253,6 +253,7 @@ formPanel setClass('empty-story-tip input-control has-prefix has-suffix'), $storyEmptyPreTip, input( + set::name(''), prop('readonly'), prop('onfocus', 'this.blur()'), ), diff --git a/module/task/zen.php b/module/task/zen.php index e71de96a8d..df6a7061af 100644 --- a/module/task/zen.php +++ b/module/task/zen.php @@ -544,12 +544,13 @@ class taskZen extends task * 处理批量创建任务的请求数据。 * Process the request data for batch create tasks. * - * @param object $execution - * @param object $formData + * @param object $execution + * @param object $formData + * @param int $taskID * @access protected * @return object[]|false */ - protected function prepareTasks4BatchCreate(object $execution, object $formData): array|false + protected function prepareTasks4BatchCreate(object $execution, object $formData, int $taskID): array|false { /* 去除重复数据。 Deduplicated data. */ $tasks = $this->removeDuplicate4BatchCreate($execution, $formData); @@ -569,6 +570,8 @@ class taskZen extends task $data = array(); foreach($formData->name as $i => $name) { + if(empty($name)) continue; + /* 给同上的变量赋值。 Assign values to ditto fields. */ $story = !isset($tasks->story[$i]) || $tasks->story[$i] == 'ditto' ? $story : $tasks->story[$i]; $module = !isset($tasks->module[$i]) || $tasks->module[$i] == 'ditto' ? $module : $tasks->module[$i]; @@ -577,19 +580,9 @@ class taskZen extends task $estStarted = !isset($tasks->estStarted[$i]) || isset($tasks->estStartedDitto[$i]) ? $estStarted : $tasks->estStarted[$i]; $deadline = !isset($tasks->deadline[$i]) || isset($tasks->deadlineDitto[$i]) ? $deadline : $tasks->deadline[$i]; - /* 检查任务名称为空的数据。 Check the data whick task's name. */ - if(empty($tasks->name[$i])) - { - if($this->common->checkValidRow('task', $tasks, $i)) - { - dao::$errors['message'][] = sprintf($this->lang->error->notempty, $this->lang->task->name); - return false; - } - continue; - } $dittoFields = array('story' => $story, 'module' => $module, 'type' => $type, 'assignedTo' => $assignedTo, 'estStarted' => $estStarted, 'deadline' => $deadline); - $data[$i] = $this->buildData4BatchCreate($execution, $tasks, $i, $dittoFields, $extendFields); + $data[$i] = $this->buildData4BatchCreate($execution, $tasks, $i, $dittoFields, $extendFields, $taskID); } return $data; @@ -652,10 +645,11 @@ class taskZen extends task * @param int $index * @param array $dittoFields * @param array $extendFields + * @param int $taskID * @access protected * @return object */ - protected function buildData4BatchCreate(object $execution, object $tasks, int $index, array $dittoFields, array $extendFields): object + protected function buildData4BatchCreate(object $execution, object $tasks, int $index, array $dittoFields, array $extendFields, int $taskID): object { extract($dittoFields); $now = helper::now(); @@ -665,10 +659,10 @@ class taskZen extends task $task->type = $type; $task->module = (int)$module; $task->assignedTo = $assignedTo; - $task->color = $tasks->color[$index]; + $task->color = isset($tasks->color[$index]) ? $tasks->color[$index] : ''; $task->name = $tasks->name[$index]; $task->desc = nl2br($tasks->desc[$index]); - $task->pri = $tasks->pri[$index]; + $task->pri = (int)$tasks->pri[$index]; $task->estimate = $tasks->estimate[$index] ? $tasks->estimate[$index] : 0; $task->left = $tasks->estimate[$index] ? $tasks->estimate[$index] : 0; $task->project = $execution->project; @@ -678,7 +672,7 @@ class taskZen extends task $task->status = 'wait'; $task->openedBy = $this->app->user->account; $task->openedDate = $now; - $task->parent = $tasks->parent[$index]; + $task->parent = $taskID; $task->vision = isset($tasks->vision[$index]) ? $tasks->vision[$index] : 'rnd'; $task->version = 1; if($story) $task->storyVersion = (int)$this->dao->findById($task->story)->from(TABLE_STORY)->fetch('version'); @@ -1103,8 +1097,8 @@ class taskZen extends task { $this->loadModel('kanban'); - $regionID = (int)$output['regionID']; - $laneID = (int)$output['laneID']; + $regionID = isset($output['regionID']) ? (int)$output['regionID'] : 0; + $laneID = isset($output['laneID']) ? (int)$output['laneID'] : 0; $regionPairs = $this->kanban->getRegionPairs($executionID, 0, 'execution'); $regionID = $regionID ? $regionID : key($regionPairs); $lanePairs = $this->kanban->getLanePairsByRegion($regionID, 'task'); @@ -1360,7 +1354,7 @@ class taskZen extends task { /* 获取区域和泳道下拉数据,并设置区域和泳道的默认值。 */ /* Get region and lane dropdown data and set default values for regions and lanes. */ - if($execution->type == 'kanban') $this->assignKanbanRelatedVars($execution->id, $output); + if($execution->type == 'kanban') $this->assignKanban4Create($execution->id, $output); if($taskID) { @@ -1390,6 +1384,7 @@ class taskZen extends task $this->view->taskConsumed = isset($task) ? $task->consumed : 0; $this->view->customFields = $customFields; $this->view->checkedFields = $checkedFields; + $this->view->hideStory = $this->task->isNoStoryExecution($execution); $this->display(); }