@@ -16,6 +16,7 @@ $lang->dot = '.';
|
||||
$lang->at = ' at ';
|
||||
$lang->downArrow = '↓';
|
||||
$lang->null = '空';
|
||||
$lang->ellipsis = '...';
|
||||
|
||||
$lang->zentaoPMS = 'zentao';
|
||||
$lang->welcome = "%s PMS";
|
||||
|
||||
@@ -16,6 +16,7 @@ $lang->dot = '。';
|
||||
$lang->at = ' 于 ';
|
||||
$lang->downArrow = '↓';
|
||||
$lang->null = '空';
|
||||
$lang->ellipsis = '…';
|
||||
|
||||
$lang->zentaoPMS = '禅道';
|
||||
$lang->welcome = "%s项目管理系统";
|
||||
|
||||
@@ -16,6 +16,7 @@ $lang->dot = '。';
|
||||
$lang->at = ' 于 ';
|
||||
$lang->downArrow = '↓';
|
||||
$lang->null = '空';
|
||||
$lang->ellipsis = '…';
|
||||
|
||||
$lang->zentaoPMS = '禪道';
|
||||
$lang->welcome = "%s項目管理系統";
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+12
-3
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;}
|
||||
@@ -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();
|
||||
|
||||
@@ -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('<option value="' + key + '">' + $selectedItem.text() + '</option>');
|
||||
}
|
||||
}
|
||||
$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('<li class="loading"><i class="icon-spin icon-spinner icon-2x"></i></li>');
|
||||
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('<li class="tip">' + result[key] + '</li>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$searchResult.append("<li><a href='javascript:;' data-key='" + key + "'>" + result[key] + "</a></li>");
|
||||
}
|
||||
}
|
||||
$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'});
|
||||
})
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 = '键入来搜索需求';
|
||||
|
||||
@@ -125,4 +125,20 @@
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
<div class='modal fade' id='searchStories'>
|
||||
<div class='modal-dialog'>
|
||||
<div class='modal-content'>
|
||||
<div class='modal-header'>
|
||||
<button type='button' class='close' data-dismiss='modal'>×</button>
|
||||
<div class='searchInput w-p90'>
|
||||
<input id='storySearchInput' type='text' class='form-control' placeholder='<?php echo $lang->testcase->searchStories?>'>
|
||||
<i class='icon icon-search'></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class='modal-body'>
|
||||
<ul id='searchResult'></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
|
||||
Reference in New Issue
Block a user