icons['bug']);?>
diff --git a/module/bug/view/confirmbug.html.php b/module/bug/view/confirmbug.html.php
index 614a266518..91a0dee309 100755
--- a/module/bug/view/confirmbug.html.php
+++ b/module/bug/view/confirmbug.html.php
@@ -13,7 +13,6 @@
bug->placeholder);
js::set('page', 'confirmbug');
?>
diff --git a/module/bug/view/create.html.php b/module/bug/view/create.html.php
index 2a92d5dd4e..2b4b38bbf3 100644
--- a/module/bug/view/create.html.php
+++ b/module/bug/view/create.html.php
@@ -13,7 +13,6 @@
bug->placeholder);
js::set('page', 'create');
diff --git a/module/bug/view/edit.html.php b/module/bug/view/edit.html.php
index 98e9be7f0b..74dc7f598b 100644
--- a/module/bug/view/edit.html.php
+++ b/module/bug/view/edit.html.php
@@ -12,7 +12,6 @@
?>
dot = '.';
$lang->at = ' at ';
$lang->downArrow = '↓';
$lang->null = '空';
+$lang->ellipsis = '...';
$lang->zentaoPMS = 'zentao';
$lang->welcome = "%s PMS";
diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php
index b695ee2ff3..270d5773b1 100644
--- a/module/common/lang/zh-cn.php
+++ b/module/common/lang/zh-cn.php
@@ -16,6 +16,7 @@ $lang->dot = '。';
$lang->at = ' 于 ';
$lang->downArrow = '↓';
$lang->null = '空';
+$lang->ellipsis = '…';
$lang->zentaoPMS = '禅道';
$lang->welcome = "%s项目管理系统";
diff --git a/module/common/lang/zh-tw.php b/module/common/lang/zh-tw.php
index f481194df5..6bc4555a82 100644
--- a/module/common/lang/zh-tw.php
+++ b/module/common/lang/zh-tw.php
@@ -16,6 +16,7 @@ $lang->dot = '。';
$lang->at = ' 于 ';
$lang->downArrow = '↓';
$lang->null = '空';
+$lang->ellipsis = '…';
$lang->zentaoPMS = '禪道';
$lang->welcome = "%s項目管理系統";
diff --git a/module/story/control.php b/module/story/control.php
index b37686b049..fae0ee5848 100644
--- a/module/story/control.php
+++ b/module/story/control.php
@@ -898,10 +898,48 @@ class story extends control
* @param int $moduleID
* @param int $storyID
* @param string $onlyOption
+ * @param string $status
+ * @param int $limit
* @access public
* @return void
*/
- public function ajaxGetProductStories($productID, $moduleID = 0, $storyID = 0, $onlyOption = 'false', $status = '')
+ public function ajaxGetProductStories($productID, $moduleID = 0, $storyID = 0, $onlyOption = 'false', $status = '', $limit = 0)
+ {
+ if($moduleID)
+ {
+ $moduleID = $this->loadModel('tree')->getStoryModule($moduleID);
+ $moduleID = $this->tree->getAllChildID($moduleID);
+ }
+
+ $storyStatus = '';
+ if($status == 'noclosed')
+ {
+ $storyStatus = $this->lang->story->statusList;
+ unset($storyStatus['closed']);
+ $storyStatus = array_keys($storyStatus);
+ }
+
+ $stories = $this->story->getProductStoryPairs($productID, $moduleID, $storyStatus, 'id_desc', $limit);
+ $select = html::select('story', $stories, $storyID, "class='form-control'");
+
+ /* If only need options, remove select wrap. */
+ if($onlyOption == 'true') die(substr($select, strpos($select, '>') + 1, -10));
+ die($select);
+ }
+
+ /**
+ * AJAX: search stories of a product as json
+ *
+ * @param string $key
+ * @param int $productID
+ * @param int $moduleID
+ * @param int $storyID
+ * @param string $status
+ * @param int $limit
+ * @access public
+ * @return void
+ */
+ public function ajaxSearchProductStories($key, $productID, $moduleID = 0, $storyID = 0, $status = 'noclosed', $limit = 50)
{
if($moduleID)
{
@@ -918,11 +956,23 @@ class story extends control
}
$stories = $this->story->getProductStoryPairs($productID, $moduleID, $storyStatus);
- $select = html::select('story', $stories, $storyID, "class='form-control'");
+ $result = array();
+ $i = 0;
+ foreach ($stories as $id => $story)
+ {
+ if($limit > 0 && $i > $limit) break;
+ if(('#' . $id) === $key || stripos($story, $key) !== false)
+ {
+ $result[$id] = $story;
+ $i++;
+ }
+ }
+ if($i < 1)
+ {
+ $result['info'] = $this->lang->noResultsMatch;
+ }
- /* If only need options, remove select wrap. */
- if($onlyOption == 'true') die(substr($select, strpos($select, '>') + 1, -10));
- die($select);
+ die(json_encode($result));
}
/**
diff --git a/module/story/model.php b/module/story/model.php
index 0b4ae99010..f50eb0fc57 100644
--- a/module/story/model.php
+++ b/module/story/model.php
@@ -945,10 +945,11 @@ class storyModel extends model
* @param array|string $moduleIds
* @param string $status
* @param string $order
+ * @param int $limit
* @access public
* @return array
*/
- public function getProductStoryPairs($productID = 0, $moduleIds = 0, $status = 'all', $order = 'id_desc')
+ public function getProductStoryPairs($productID = 0, $moduleIds = 0, $status = 'all', $order = 'id_desc', $limit = 0)
{
$stories = $this->dao->select('t1.id, t1.title, t1.module, t1.pri, t1.estimate, t2.name AS product')
->from(TABLE_STORY)->alias('t1')->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id')
@@ -960,7 +961,7 @@ class storyModel extends model
->orderBy($order)
->fetchAll();
if(!$stories) return array();
- return $this->formatStories($stories);
+ return $this->formatStories($stories, 'full', $limit);
}
/**
@@ -1379,10 +1380,11 @@ class storyModel extends model
*
* @param array $stories
* @param string $type
+ * @param int $limit
* @access public
* @return void
*/
- public function formatStories($stories, $type = 'full')
+ public function formatStories($stories, $type = 'full', $limit = 0)
{
/* Get module names of stories. */
/*$modules = array();
@@ -1391,6 +1393,7 @@ class storyModel extends model
/* Format these stories. */
$storyPairs = array('' => '');
+ $i = 0;
foreach($stories as $story)
{
if($type == 'short')
@@ -1402,6 +1405,12 @@ class storyModel extends model
$property = '(' . $this->lang->story->pri . ':' . $story->pri . ',' . $this->lang->story->estimate . ':' . $story->estimate . ')';
}
$storyPairs[$story->id] = $story->id . ':' . $story->title . $property;
+
+ if($limit > 0 && ++$i > $limit)
+ {
+ $storyPairs['showmore'] = $this->lang->more . $this->lang->ellipsis;
+ break;
+ }
}
return $storyPairs;
}
diff --git a/module/task/view/batchcreate.html.php b/module/task/view/batchcreate.html.php
index 960d66fb11..3007f38eea 100644
--- a/module/task/view/batchcreate.html.php
+++ b/module/task/view/batchcreate.html.php
@@ -11,7 +11,6 @@
*/
?>
-
task->batchCreate);?>
diff --git a/module/task/view/edit.html.php b/module/task/view/edit.html.php
index 92b0d351fb..8ae7a7eafc 100644
--- a/module/task/view/edit.html.php
+++ b/module/task/view/edit.html.php
@@ -12,7 +12,6 @@
?>
-
story); ?>
task->confirmChangeProject); ?>
diff --git a/module/testcase/control.php b/module/testcase/control.php
index 2bbcbb5eb9..6276af08dc 100644
--- a/module/testcase/control.php
+++ b/module/testcase/control.php
@@ -317,7 +317,7 @@ class testcase extends control
$modules = $this->loadModel('tree')->getStoryModule($currentModuleID);
$modules = $this->tree->getAllChildID($modules);
}
- $stories = $this->story->getProductStoryPairs($productID, $modules, array_keys($storyStatus));
+ $stories = $this->story->getProductStoryPairs($productID, $modules, array_keys($storyStatus), 'id_desc', 20);
$this->view->title = $title;
$this->view->caseTitle = $caseTitle;
diff --git a/module/testcase/css/create.css b/module/testcase/css/create.css
new file mode 100644
index 0000000000..5f081cdc55
--- /dev/null
+++ b/module/testcase/css/create.css
@@ -0,0 +1,12 @@
+#searchStories .searchInput {position: relative;}
+#searchStories .searchInput .icon {position: absolute; display: block; left: 9px; top: 9px; z-index: 5; color: #808080;}
+#storySearchInput {padding-left: 30px;}
+
+#searchStories .modal-body {height: 300px; overflow-y: auto; padding: 0;}
+#searchResult {padding-left: 0; list-style: none; width: 100%}
+#searchResult > li {display: block}
+#searchResult > li.tip {padding: 6px 15px; color: #808080}
+#searchResult > li.loading {text-align: center; padding: 50px}
+#searchResult > li.loading > .icon-spinner:before {font-size: 28px;}
+#searchResult > li > a {display: block; padding: 6px 15px; color: #333; border-bottom: 1px solid #e5e5e5}
+#searchResult > li > a:hover, #searchResult > li > a.selected {color: #1a4f85; background-color: #ddd;}
diff --git a/module/testcase/js/common.js b/module/testcase/js/common.js
index ee76c11f10..83371b56e7 100644
--- a/module/testcase/js/common.js
+++ b/module/testcase/js/common.js
@@ -47,7 +47,7 @@ function setStories()
{
moduleID = $('#module').val();
productID = $('#product').val();
- link = createLink('story', 'ajaxGetProductStories', 'productID=' + productID + '&moduleID=' + moduleID + '&storyID=0&onlyOption=false&status=noclosed');
+ link = createLink('story', 'ajaxGetProductStories', 'productID=' + productID + '&moduleID=' + moduleID + '&storyID=0&onlyOption=false&status=noclosed&limit=20');
$.get(link, function(stories)
{
var value = $('#story').val();
@@ -113,7 +113,7 @@ function createRow()
newRow += "
| ";
newRow += "
| ";
newRow += "
| ";
- newRow += "
";
+ newRow += " ";
newRow += " ";
newRow += " ";
newRow += " ";
diff --git a/module/testcase/js/create.js b/module/testcase/js/create.js
index 46f32ecd55..89a4f4f301 100644
--- a/module/testcase/js/create.js
+++ b/module/testcase/js/create.js
@@ -17,6 +17,109 @@ function setPreview()
$(function()
{
- $("#story").chosen(defaultChosenOptions);
- $("#preview").modalTrigger({width:960, type:'iframe'});
+ var $searchStories = $('#searchStories');
+ var lastSearchFn = false;
+ var $searchInput = $('#storySearchInput');
+ var $searchResult = $('#searchResult');
+ var $selectedItem;
+ var $story = $('#story');
+ $(document).on('change', '#story', function()
+ {
+ if($(this).val() === 'showmore')
+ {
+ $searchStories.modal('show').on('shown.zui.modal', function(){$searchInput.focus();});
+ }
+ });
+
+ $searchStories.on('hide.zui.modal', function()
+ {
+ var key = '';
+ if($selectedItem && $selectedItem.length)
+ {
+ key = $selectedItem.data('key');
+ if(!$story.children('option[value="' + key + '"]').length)
+ {
+ $story.prepend(' ');
+ }
+ }
+ $story.val(key).trigger("chosen:updated");
+ $selectedItem = null;
+ });
+
+ var selectItem = function(item)
+ {
+ $selectedItem = $(item).first();
+ $searchStories.modal('hide');
+ };
+
+ $searchResult.on('click', 'a', function(){selectItem(this);}).on('mouseenter', 'a', function()
+ {
+ $searchResult.find('a.selected').removeClass('selected');
+ $(this).addClass('selected');
+ }).on('mouseleave', 'a', function()
+ {
+ $(this).removeClass('selected');
+ });
+
+ $searchInput.on('paste change keyup', function()
+ {
+ if(lastSearchFn) clearTimeout(lastSearchFn);
+ lastSearchFn = setTimeout(function()
+ {
+ var key = $searchInput.val() || '';
+ if(key && key != $searchInput.data('lastkey'))
+ {
+ $searchResult.empty().append(' ');
+ var link = createLink('story', 'ajaxSearchProductStories', 'key=' + key + '&productID=' + $('#product').val() + '&moduleID=' + $('#module').val() + '&storyID=0&status=noclosed&limit=20');
+ $.getJSON(link, function(result)
+ {
+ $searchResult.empty();
+ if(result)
+ {
+ for(var key in result)
+ {
+ if(key === 'info')
+ {
+ $searchResult.append(' ' + result[key] + '');
+ }
+ else
+ {
+ $searchResult.append(" " + result[key] + "");
+ }
+ }
+ $searchResult.find('li:first > a').addClass('selected');
+ }
+ });
+ $searchInput.data('lastkey', key);
+ }
+ else if(!key.length)
+ {
+ $searchResult.empty();
+ }
+ }, 500);
+ }).on('keyup', function(e)
+ {
+ var $selected = $searchResult.find('a.selected').first();
+ if(e.keyCode == 38) // keyup
+ {
+ var $prev = $selected.closest('li').prev().children('a');
+ if($prev.length)
+ {
+ $selected.removeClass('selected');
+ $prev.addClass('selected');
+ }
+ }
+ else if(e.keyCode == 40) // keydown
+ {
+ var $next = $selected.closest('li').next().children('a');
+ if($next.length)
+ {
+ $selected.removeClass('selected');
+ $next.addClass('selected');
+ }
+ }
+ else if(e.keyCode == 13) selectItem($selected);
+ });
+
+ $("#preview").modalTrigger({width:960, type:'iframe'});
})
diff --git a/module/testcase/lang/en.php b/module/testcase/lang/en.php
index 7d5ce36a45..086fd21f8f 100644
--- a/module/testcase/lang/en.php
+++ b/module/testcase/lang/en.php
@@ -156,3 +156,5 @@ $lang->testcase->buttonToList = 'Back';
$lang->testcase->errorEncode = 'No data, please select right encode and upload again!';
$lang->testcase->noFunction = 'Iconv and mb_convert_encoding does not exist, you can not turn the data into the desired coding!';
$lang->testcase->noRequire = "In the row of %s, the %s is a required field";
+
+$lang->testcase->searchStories = 'Type to search stories';
diff --git a/module/testcase/lang/zh-cn.php b/module/testcase/lang/zh-cn.php
index f6d5b94a72..b51346c553 100644
--- a/module/testcase/lang/zh-cn.php
+++ b/module/testcase/lang/zh-cn.php
@@ -156,3 +156,5 @@ $lang->testcase->buttonToList = '返回';
$lang->testcase->errorEncode = '无数据,请选择正确的编码重新上传!';
$lang->testcase->noFunction = '不存在iconv和mb_convert_encoding转码方法,不能将数据转成想要的编码!';
$lang->testcase->noRequire = "%s行的“%s”是必填字段,不能为空";
+
+$lang->testcase->searchStories = '键入来搜索需求';
diff --git a/module/testcase/view/create.html.php b/module/testcase/view/create.html.php
index 1ca6f3ab0e..0d3e679322 100644
--- a/module/testcase/view/create.html.php
+++ b/module/testcase/view/create.html.php
@@ -12,7 +12,6 @@
?>
-
testcase->deleteStep);?>
testcase->insertBefore);?>
testcase->insertAfter);?>
@@ -126,4 +125,20 @@
+
diff --git a/module/testcase/view/edit.html.php b/module/testcase/view/edit.html.php
index 8873497191..e27e8e7062 100644
--- a/module/testcase/view/edit.html.php
+++ b/module/testcase/view/edit.html.php
@@ -11,7 +11,6 @@
*/
?>
-
testcase->deleteStep);?>
testcase->insertBefore);?>
diff --git a/module/tree/view/edit.html.php b/module/tree/view/edit.html.php
index 862351aad8..929f4ce890 100644
--- a/module/tree/view/edit.html.php
+++ b/module/tree/view/edit.html.php
@@ -14,7 +14,6 @@
$webRoot = $this->app->getWebRoot();
$jsRoot = $webRoot . "js/";
?>
-
diff --git a/module/user/view/batchcreate.html.php b/module/user/view/batchcreate.html.php
index 6e486a6c55..eed8f89eaf 100644
--- a/module/user/view/batchcreate.html.php
+++ b/module/user/view/batchcreate.html.php
@@ -11,7 +11,6 @@
*/
?>
-
diff --git a/module/user/view/batchedit.html.php b/module/user/view/batchedit.html.php
index 0d872b8f3c..7d596ffbea 100644
--- a/module/user/view/batchedit.html.php
+++ b/module/user/view/batchedit.html.php
@@ -11,7 +11,6 @@
*/
?>
-
| |