diff --git a/module/project/css/tree.css b/module/project/css/tree.css
index 69efbce00f..ffa0a9a4f5 100644
--- a/module/project/css/tree.css
+++ b/module/project/css/tree.css
@@ -5,9 +5,10 @@
#projectTree li > .tree-item-wrapper > .tree-toggle {display: block}
#projectTree li > .tree-item-wrapper:hover {display: block; background-color: #edf3fe; cursor: pointer; margin-left: -20px; padding-left: 20px;}
#projectTree li {line-height: 30px; padding-top: 0; padding-bottom: 0}
-#projectTree .item-type-product > .tree-item-wrapper {display: block; background-color: #edf3fe; margin-left: -20px; padding-left: 20px; border-bottom: 1px solid #ddd; cursor: pointer}
-#projectTree .item-type-product > .tree-item-wrapper:hover {background-color: #eee}
-#projectTree .item-type-product.open > .tree-item-wrapper {border-bottom: none}
+#projectTree > li > .tree-item-wrapper {display: block; background-color: #edf3fe; margin-left: -20px; padding-left: 20px; border-bottom: 1px solid #ddd; cursor: pointer}
+#projectTree > li > .tree-item-wrapper:hover {background-color: #eee}
+#projectTree > li.open > .tree-item-wrapper {border-bottom: none}
+#projectTree > li.open + li > .tree-item-wrapper {border-top: 1px solid #ddd}
#projectTree.tree li>.list-toggle:before {content: '\e6bb'}
#projectTree.tree li.open>.list-toggle:before {content: '\e6b8';}
#projectTree.tree li>.list-toggle {top: 2px}
@@ -18,3 +19,5 @@
.tree-view-btn {min-width: 36px;}
.main > .panel > .panel-heading > .panel-actions {margin-top: -8px; margin-right: -12px;}
+
+.label-task-count {background: #ddd; color: #333}
diff --git a/module/project/lang/en.php b/module/project/lang/en.php
index 1ba9e345ec..4a07f4eb11 100644
--- a/module/project/lang/en.php
+++ b/module/project/lang/en.php
@@ -108,6 +108,7 @@ $lang->project->team = 'Team';
$lang->project->doc = 'Doc';
$lang->project->manageProducts = 'Link ' . $lang->productCommon;
$lang->project->linkStory = 'Link story';
+$lang->project->unlinkStoryTasks = 'Tasks of unlink story';
$lang->project->view = "Info";
$lang->project->create = "Add";
$lang->project->copy = "Copy a {$lang->projectCommon}";
@@ -256,4 +257,3 @@ $lang->project->treeLevel['product'] = array('icon' => 'icon-cube', 'text' => 'P
$lang->project->treeLevel['module'] = array('icon' => 'icon-bookmark-empty', 'text' => 'Modules view');
$lang->project->treeLevel['story'] = array('icon' => 'icon-lightbulb', 'text' => 'Stories view');
$lang->project->treeLevel['task'] = array('icon' => 'icon-tasks', 'text' => 'Tasks view');
-$lang->project->treeLevel['custom'] = array('icon' => 'icon-align-left', 'text' => 'Custom view');
diff --git a/module/project/lang/zh-cn.php b/module/project/lang/zh-cn.php
index 92753d9edc..8a324538eb 100644
--- a/module/project/lang/zh-cn.php
+++ b/module/project/lang/zh-cn.php
@@ -108,6 +108,7 @@ $lang->project->team = '团队成员';
$lang->project->doc = '文档列表';
$lang->project->manageProducts = '关联' . $lang->productCommon;
$lang->project->linkStory = '关联需求';
+$lang->project->unlinkStoryTasks = '未关联需求任务';
$lang->project->view = "{$lang->projectCommon}概况";
$lang->project->create = "添加{$lang->projectCommon}";
$lang->project->copy = "复制{$lang->projectCommon}";
@@ -260,4 +261,3 @@ $lang->project->treeLevel['product'] = array('icon' => 'icon-cube', 'text' => '
$lang->project->treeLevel['module'] = array('icon' => 'icon-bookmark-empty', 'text' => '模块视图');
$lang->project->treeLevel['story'] = array('icon' => 'icon-lightbulb', 'text' => '需求视图');
$lang->project->treeLevel['task'] = array('icon' => 'icon-tasks', 'text' => '任务视图');
-$lang->project->treeLevel['custom'] = array('icon' => 'icon-align-left', 'text' => '自定义');
diff --git a/module/project/model.php b/module/project/model.php
index 889b3f1aa4..2bc7f863e9 100644
--- a/module/project/model.php
+++ b/module/project/model.php
@@ -1944,6 +1944,54 @@ class projectModel extends model
return $chartData;
}
+ /**
+ * Format task list for tree view
+ * @param array $tasks
+ * @access public
+ * @return array
+ */
+ public function formatTasksForTree($tasks)
+ {
+ static $users;
+ if(empty($users)) $users = $this->loadModel('user')->getPairs('noletter');
+
+ $taskItems = array();
+ foreach ($tasks as $task)
+ {
+ $taskItem = new stdclass();
+ $taskItem->type = 'task';
+ $taskItem->id = $task->id;
+ $taskItem->title = $task->name;
+ $taskItem->color = $task->color;
+ $taskItem->pri = (int) $task->pri;
+ $taskItem->status = $task->status;
+ $taskItem->estimate = $task->estimate;
+ $taskItem->consumed = $task->consumed;
+ $taskItem->left = $task->left;
+ $taskItem->assignedTo = $users[$task->assignedTo];
+ $taskItem->url = helper::createLink('task', 'view', "task=$task->id");
+ $taskItem->storyChanged = $task->storyStatus == 'active' and $task->latestStoryVersion > $task->storyVersion;
+
+ $buttons = '';
+ $buttons .= common::buildIconButton('task', 'assignTo', "projectID=$task->project&taskID=$task->id", $task, 'list', '', '', 'iframe', true);
+ $buttons .= common::buildIconButton('task', 'start', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
+
+ $buttons .= common::buildIconButton('task', 'recordEstimate', "taskID=$task->id", $task, 'list', 'time', '', 'iframe', true);
+ if($browseType == 'needconfirm')
+ {
+ $lang->task->confirmStoryChange = $lang->confirm;
+ $buttons .= common::buildIconButton('task', 'confirmStoryChange', "taskid=$task->id", '', 'list', '', 'hiddenwin');
+ }
+ $buttons .= common::buildIconButton('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
+ $buttons .= common::buildIconButton('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
+ $buttons .= common::buildIconButton('task', 'edit', "taskID=$task->id", '', 'list');
+
+ $taskItem->buttons = $buttons;
+ $taskItems[] = $taskItem;
+ }
+ return $taskItems;
+ }
+
/**
* Build product task tree item
* @param object $tree
@@ -1991,41 +2039,8 @@ class projectModel extends model
$storyTasks = $this->task->getStoryTasks($story->id, $projectID);
if(!empty($storyTasks))
{
- $taskItems = array();
- foreach ($storyTasks as $task)
- {
- $taskItem = new stdclass();
- $taskItem->type = 'task';
- $taskItem->id = $task->id;
- $taskItem->title = $task->name;
- $taskItem->color = $task->color;
- $taskItem->pri = $task->pri;
- $taskItem->status = $task->status;
- $taskItem->estimate = $task->estimate;
- $taskItem->consumed = $task->consumed;
- $taskItem->left = $task->left;
- $taskItem->assignedTo = $users[$task->assignedTo];
- $taskItem->url = helper::createLink('task', 'view', "task=$task->id");
- $taskItem->storyChanged = $task->storyStatus == 'active' and $task->latestStoryVersion > $task->storyVersion;
-
- $buttons = '';
- $buttons .= common::buildIconButton('task', 'assignTo', "projectID=$task->project&taskID=$task->id", $task, 'list', '', '', 'iframe', true);
- $buttons .= common::buildIconButton('task', 'start', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
-
- $buttons .= common::buildIconButton('task', 'recordEstimate', "taskID=$task->id", $task, 'list', 'time', '', 'iframe', true);
- if($browseType == 'needconfirm')
- {
- $lang->task->confirmStoryChange = $lang->confirm;
- $buttons .= common::buildIconButton('task', 'confirmStoryChange', "taskid=$task->id", '', 'list', '', 'hiddenwin');
- }
- $buttons .= common::buildIconButton('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
- $buttons .= common::buildIconButton('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
- $buttons .= common::buildIconButton('task', 'edit', "taskID=$task->id", '', 'list');
-
- $taskItem->buttons = $buttons;
- $taskItems[] = $taskItem;
- }
-
+ $taskItems = $this->formatTasksForTree($storyTasks);
+ $storyItem->tasksCount = count($taskItems);
$storyItem->children = array();
$storyItem->children[] = array('id' => 'tasks' . $story->id, 'tasks' => $taskItems, 'type' => 'tasks', 'actions' => false);
}
@@ -2056,6 +2071,20 @@ class projectModel extends model
$tree[] = $this->buildProductTaskTree($productItem, $projectID);
}
+ $zeroStoryTasks = $this->loadModel('task')->getStoryTasks(0, $projectID);
+ if(count($zeroStoryTasks))
+ {
+ $zeroStoryTasks = $this->formatTasksForTree($zeroStoryTasks);
+ $unlinkStoryItem = new stdclass();
+ $unlinkStoryItem->type = 'unlinkStory';
+ $unlinkStoryItem->id = 'unlinkStory';
+ $unlinkStoryItem->tasksCount = count($zeroStoryTasks);
+ $unlinkStoryItem->title = $this->lang->project->unlinkStoryTasks;
+ $unlinkStoryItem->children = array();
+ $unlinkStoryItem->actions = false;
+ $unlinkStoryItem->children[] = array('id' => 'tasks0', 'tasks' => $zeroStoryTasks, 'type' => 'tasks', 'actions' => false);
+ $tree[] = $unlinkStoryItem;
+ }
return $tree;
}
}
diff --git a/module/project/view/tree.html.php b/module/project/view/tree.html.php
index b4b38d3cb0..43eac6cceb 100644
--- a/module/project/view/tree.html.php
+++ b/module/project/view/tree.html.php
@@ -46,7 +46,6 @@ $(function()
var hoursFormat = 'project->hours ?>';
var viewLevel = '' || 'custom';
var data = $.parseJSON('');
- console.log('data', data);
var $tree = $('#projectTree');
var statusMap = $.parseJSON('task->statusList);?>');
var selectCustomLevel = function() {$('.tree-view-btn.active').removeClass('active').filter('[data-type="custom"]').addClass('active');};
@@ -82,7 +81,11 @@ $(function()
}
else if(item.type === 'story')
{
- $li.append(' ').append($('').attr({href: item.url}).text('#' + item.storyId + ' ' + item.title).css('color', item.color));
+ $li.append(' ').append($('').attr({href: item.url}).text('#' + item.storyId + ' ' + item.title).css('color', item.color));
+ if(item.children && item.children.length)
+ {
+ if(item.tasksCount) $li.append(' ' + item.tasksCount + '');
+ }
}
else if(item.type === 'task')
{
@@ -97,10 +100,10 @@ $(function()
$.each(item.tasks, function(idx, task)
{
var $tr = $('');
- $tr.append($('