Merge branch 'sprint/182_liumengyi_kanban' into 'kanban'

* Finish task #47334.Add card view.

See merge request easycorp/zentaopms!1372
This commit is contained in:
孙广明
2022-01-13 03:06:47 +00:00
8 changed files with 628 additions and 387 deletions
+1 -1
View File
@@ -2638,7 +2638,7 @@ class execution extends control
$this->loadModel('product');
/* Get projects, executions and products. */
$object = $this->project->getByID($objectID, $this->app->tab == 'project' ? 'project' : 'sprint,stage');
$object = $this->project->getByID($objectID, $this->app->tab == 'project' ? 'project' : 'sprint,stage,kanban');
$products = $this->product->getProducts($objectID);
$browseLink = $this->createLink($this->app->tab == 'project' ? 'projectstory' : 'execution', 'story', "objectID=$objectID");
+24 -30
View File
@@ -58,38 +58,8 @@
.region .kanban-lane-items {overflow: auto; padding-bottom: 10px;}
.region .kanban-item {position: relative}
.region .kanban-item > .kanban-card > .title:hover {color: #2272eb}
.region .kanban-item > .kanban-card > .info {margin-top: 5px; position: relative;}
.region .kanban-item > .kanban-card > .info > .pri {height: 14px; border-width: 1px; font-size: 12px; line-height: 12px; min-width: 14px; padding: 0 1px;}
.region .kanban-item > .kanban-card > .info > .estimate{margin-left: 4px; color: #999; background-color: rgb(255, 255, 255); padding: 3px 0px}
.region .kanban-item > .kanban-card > .info > .time {margin-left: 4px; font-size: 12px; background: rgb(242, 242, 242)}
.region .kanban-item > .kanban-card > .info > .user {position: absolute; right: 0; top: -2px}
.region .kanban-item > .kanban-card > .info > .user > .avatar {display: inline-block; width: 24px; height: 24px; line-height: 24px; margin-right: 0px; margin-left: 2px}
.region .kanban-item > .kanban-card > .info > .user > span {display: inline-block; color: transparent; width: 2px; height: 2px; background-color: #8990a2; position: sticky; border-radius: 50%; top: 3px; margin: 0 7px;}
.region .kanban-item > .kanban-card > .info > .user > span:before,
.region .kanban-item > .kanban-card > .info > .user > span:after {content: ''; display: block; position: absolute; width: 2px; height: 2px; background-color: #8990a2; top: 0; border-radius: 50%}
.region .kanban-item > .kanban-card > .info > .user > span:before {left: -4px;}
.region .kanban-item > .kanban-card > .info > .user > span:after {right: -4px;}
.region .kanban-item > .kanban-card > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.region .kanban-item > .kanban-card > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.region .kanban-item:hover > .actions {opacity: 1;}
.region .kanban-item .has-color .title,
.region .kanban-item .has-color .actions .icon-more-v,
.region .kanban-item .has-color .info > .label-pri,
.region .kanban-item .has-color .info > .estimate,
.region .kanban-item .has-color .info > .user .lable-teamSumCount,
.region .kanban-item .has-color .info > .label-light {color: #FFFFFF;}
.region .kanban-item .has-color .info > .label-pri {border-color: #FFFFFF;}
.region .kanban-item .item-more {position: absolute; right: 6px; top: 0px;}
.kanban-card {height: auto !important; padding: 8px 14px !important}
.kanban-card > .title {word-break: break-all;}
.kanban-card > .info {position: relative; margin-top: 5px}
.kanban-card > .info > .info + .info {margin-left: 8px}
.kanban-card:hover > .actions {opacity: 1;}
.kanban-card > .actions {position: absolute; top: 4px; right: 4px; opacity: 0;}
.kanban-card > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.kanban-card > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.cardcolor {width:40px; height: 14px; float: left; margin-left: 5px; margin-right: 5px; margin-top: 2px; border-radius: 2px;}
#cardcolormenu {padding:2px 2px; width: 100px;}
@@ -109,3 +79,27 @@
#kanbanScaleControl > .input-group-btn:last-child > .btn {border-radius: 0 16px 16px 0; border-left-color: transparent;}
#kanbanScaleControl > .input-group-btn > .btn:hover {border-color: #b8bfce;}
#kanbanActionMenu {top: 24px; right: 28px;}
.kanban + .kanban {margin-top: 15px}
.kanban-card > .title {display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis}
.kanban-card > .infos {position: relative; margin-top: 5px}
.kanban-card > .infos > .info + .info {margin-left: 8px}
.kanban-card > .infos > .info-id,
.kanban-card > .infos > .info-deadline,
.kanban-card > .infos > .info-estimate {font-size: 12px; position: relative; top: 2px}
.kanban-card > .infos > .label-pri {min-width: 16px; line-height: 14px; height: 16px; padding: 0}
.kanban-card > .infos > .label-severity {transform: scale(.75)}
.kanban-card > .infos > .avatar {position: absolute; right: 0; top: 0}
.kanban-card > .actions {position: absolute; top: 4px; right: 4px; opacity: 0;}
.kanban-card:hover > .actions {opacity: 1;}
.kanban-card > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.kanban-card > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
#kanban .kanban-card[data-scale-size="4"] {padding: 8px;}
#kanban .kanban-card[data-scale-size="3"] {padding: 3px 4px;}
#kanban .kanban-card[data-scale-size="3"] > .title {white-space: normal; max-height: 100%;}
#kanban .kanban-card[data-scale-size="2"] {padding: 3px 4px; overflow: hidden;}
#kanban .kanban-card[data-scale-size="2"] > .title {display: inline; line-height: 18px; white-space: normal; max-height: 100%; word-break: break-all;}
#kanban .kanban-card[data-scale-size="2"] > .infos {display: inline-block; height: 18px; vertical-align: top; margin: 0;}
#kanban .kanban-card[data-scale-size="2"] > .infos > .info-pri {transform: scale(.8); vertical-align: top;}
#kanban .kanban-card[data-scale-size="2"] > .infos > .avatar {display: inline-block; transform: scale(.8); position: relative; top: -2px; vertical-align: top; margin-right: -1px;}
+319 -144
View File
@@ -11,7 +11,7 @@ function changeKanbanScaleSize(newScaleSize)
$('#kanbanScaleControl .btn[data-type="+"]').attr('disabled', newScaleSize >= 4 ? 'disabled' : null);
$('#kanbanScaleControl .btn[data-type="-"]').attr('disabled', newScaleSize <= 1 ? 'disabled' : null);
$('#kanbans').children('.kanban').each(function()
$('#kanban').children('.region').children("div[id^='kanban']").each(function()
{
var kanban = $(this).data('zui.kanban');
if(!kanban) return;
@@ -21,6 +21,12 @@ function changeKanbanScaleSize(newScaleSize)
return newScaleSize;
}
/** Get card height */
function getCardHeight()
{
return [59, 59, 62, 62, 47][window.kanbanScaleSize];
}
$('#type').change(function()
{
var type = $('#type').val();
@@ -70,7 +76,6 @@ function createColumnMenu(options)
var items = [];
if(privs.includes('setColumn')) items.push({label: kanbanLang.editColumn, icon: 'edit', url: createLink('kanban', 'setColumn', 'columnID=' + column.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal'}});
if(privs.includes('setWIP')) items.push({label: kanbanLang.setWIP, icon: 'alert', url: createLink('kanban', 'setWIP', 'columnID=' + column.id), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width' : '500px'}});
if(privs.includes('deleteColumn') && column.$kanbanData.columns.length > 1) items.push({label: kanbanLang.deleteColumn, icon: 'trash', url: createLink('kanban', 'deleteColumn', 'columnID=' + column.id), attrs: {'target': 'hiddenwin'}});
var bounds = options.$trigger[0].getBoundingClientRect();
items.$options = {x: bounds.right, y: bounds.top};
@@ -91,7 +96,7 @@ function createColumnCreateMenu(options)
{
if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID)});
if(priv.canBatchCreateStory) items.push({label: executionLang.batchCreateStroy, url: $.createLink('story', 'batchcreate', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&executionID=' + executionID)});
if(priv.canLinkStory) items.push({label: executionLang.linkStory, url: $.createLink('execution', 'linkStory', 'executionID=' + executionID)});
if(priv.canLinkStory) items.push({label: executionLang.linkStory, url: $.createLink('execution', 'linkStory', 'executionID=' + executionID + 'objectType=kanban'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canLinkStoryByPlane) items.push({label: executionLang.linkStoryByPlan, url: '#linkStoryByPlan', 'attrs' : {'data-toggle': 'modal'}});
}
else if(col.type == 'unconfirmed')
@@ -126,35 +131,327 @@ function handleFinishDrop()
$('.kanban').find('.can-drop-here').removeClass('can-drop-here');
}
/**
/* Define drag and drop rules */
if(!window.kanbanDropRules)
{
window.kanbanDropRules =
{
story:
{
backlog: ['ready'],
ready: ['backlog'],
},
bug:
{
'unconfirmed': ['confirmed', 'fixing', 'fixed'],
'confirmed': ['fixing', 'fixed'],
'fixing': ['fixed'],
'fixed': ['testing', 'tested', 'fixing'],
'testing': ['tested', 'closed', 'fixing'],
'tested': ['closed', 'fixing'],
'closed': ['fixing'],
},
task:
{
'wait': ['developing', 'developed', 'canceled', 'closed'],
'developing': ['developed', 'pause'],
'developed': ['canceled', 'closed'],
'pause': ['developing'],
'canceled': ['developing'],
'closed': ['developing'],
}
}
}
/*
* Find drop columns
* @param {JQuery} $element Drag element
* @param {JQuery} $root Dnd root element
*/
function findDropColumns($element, $root)
{
var $task = $element;
var task = $task.data('task');
//var status = task.status;
var $col = $task.closest('.kanban-col');
var col = $col.data();
var lane = $col.closest('.kanban-lane').data('lane');
var allStatusCanChange = statusChangeMap[status];
var $col = $element.closest('.kanban-col');
var col = $col.data();
var kanbanID = $root.data('id');
var kanbanRules = window.kanbanDropRules ? window.kanbanDropRules[kanbanID] : null;
hideKanbanAction();
if(!kanbanRules) return $root.find('.kanban-lane-col:not([data-type="' + col.type + '"])');
return $root.find('.kanban-lane-col:not([data-type="EMPTY"],[data-type=""])').filter(function()
var colRules = kanbanRules[col.type];
var lane = $col.closest('.kanban-lane').data('lane');
return $root.find('.kanban-lane-col').filter(function()
{
if(!colRules) return false;
if(colRules === true) return true;
var $newCol = $(this);
var newCol = $newCol.data();
if(newCol.id === col.id) return false;
var $newLane = $newCol.closest('.kanban-lane');
var newLane = $newLane.data('lane');
$newCol.addClass('can-drop-here');
return true;
var canDropHere = colRules.indexOf(newCol.type) > -1 && newLane.id === lane.id;
if(canDropHere) $newCol.addClass('can-drop-here');
return canDropHere;
});
}
/**
* Render user avatar
* @param {String|{account: string, avatar: string}} user User account or user object
* @returns {string}
*/
function renderUserAvatar(user, objectType, objectID, size)
{
var avatarSizeClass = 'avatar-' + (size || 'sm');
var $noPrivAndNoAssigned = $('<div class="avatar has-text ' + avatarSizeClass + ' avatar-circle" title="' + noAssigned + '" style="background: #ccc"><i class="icon icon-person"></i></div>');
if(objectType == 'task')
{
if(!priv.canAssignTask && !user) return $noPrivAndNoAssigned;
var link = createLink('task', 'assignto', 'executionID=' + executionID + '&id=' + objectID, '', true);
}
if(objectType == 'story')
{
if(!priv.canAssignStory && !user) return $noPrivAndNoAssigned;
var link = createLink('story', 'assignto', 'id=' + objectID, '', true);
}
if(objectType == 'bug')
{
if(!priv.canAssignBug && !user) return $noPrivAndNoAssigned;
var link = createLink('bug', 'assignto', 'id=' + objectID, '', true);
}
if(!user) return $('<a class="avatar has-text ' + avatarSizeClass + ' avatar-circle iframe" title="' + noAssigned + '" style="background: #ccc" href="' + link + '" data-toggle="modal" data-width="80%"><i class="icon icon-person"></i></a>');
if(typeof user === 'string') user = {account: user};
if(!user.avatar && window.userList && window.userList[user.account]) user = {avatar: userList[user.account].avatar, account: user.account};
var $noPrivAvatar = $('<div class="avatar has-text ' + avatarSizeClass + ' avatar-circle" />').avatar({user: user});
if(objectType == 'task' && !priv.canAssignTask) return $noPrivAvatar;
if(objectType == 'story' && !priv.canAssignStory) return $noPrivAvatar;
if(objectType == 'bug' && !priv.canAssignBug) return $noPrivAvatar;
return $('<a class="avatar has-text ' + avatarSizeClass + ' avatar-circle iframe" title="' + user.account + '" href="' + link + '"/>').avatar({user: user}).attr('data-toggle', 'modal').attr('data-width', '80%');
}
/**
* Render deadline
* @param {String|Date} deadline Deadline
* @returns {JQuery}
*/
function renderDeadline(deadline)
{
if(deadline == '0000-00-00') return;
var date = $.zui.createDate(deadline);
var now = new Date();
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);
now.setMilliseconds(0);
var isEarlyThanToday = date.getTime() < now.getTime();
var deadlineDate = $.zui.formatDate(date, 'MM-dd');
return $('<span class="info info-deadline"/>').text(deadlineLang + ' ' + deadlineDate).addClass(isEarlyThanToday ? 'text-red' : 'text-muted');
}
/**
* Render story item
* @param {Object} item Story item object
* @param {JQuery} $item Kanban item element
* @param {Object} col Column object
* @returns {JQuery} $item Kanban item element
*/
function renderStoryItem(item, $item, col)
{
var scaleSize = window.kanbanScaleSize;
if(+$item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize);
if(scaleSize <= 3)
{
var $title = $item.find('.title');
if(!$title.length)
{
$title = $('<a class="title iframe" data-width="95%">' + (scaleSize <= 1 ? '<i class="icon icon-lightbulb text-muted"></i> ' : '') + '<span class="text"></span></a>')
.attr('href', $.createLink('story', 'view', 'storyID=' + item.id, '', true)).attr('data-toggle', 'modal').attr('data-width', '80%');
$title.appendTo($item);
}
$title.attr('title', item.title).find('.text').text(item.title);
}
if(scaleSize <= 2)
{
var idHtml = scaleSize <= 1 ? ('<span class="info info-id text-muted">#' + item.id + '</span>') : '';
var priHtml = '<span class="info info-pri label-pri label-pri-' + item.pri + '" title="' + item.pri + '">' + item.pri + '</span>';
var hoursHtml = (item.estimate && scaleSize <= 1) ? ('<span class="info info-estimate text-muted">' + item.estimate + 'h</span>') : '';
var avatarHtml = renderUserAvatar(item.assignedTo, 'story', item.id);
var $infos = $item.find('.infos');
if(!$infos.length) $infos = $('<div class="infos"></div>');
$infos.html([idHtml, priHtml, hoursHtml].join(''));
$infos[scaleSize <= 1 ? 'append' : 'prepend'](avatarHtml);
if(scaleSize <= 1) $infos.appendTo($item);
else if(scaleSize === 2) $infos.prependTo($item);
else $infos.prependTo($item.find('.title'));
}
else if(scaleSize === 4)
{
$item.html(renderUserAvatar(item.assignedTo, 'story', item.id, 'md'));
}
if(scaleSize <= 1)
{
var $actions = $item.find('.actions');
if(!$actions.length && item.menus && item.menus.length)
{
$actions = $([
'<div class="actions">',
'<a data-contextmenu="story" data-col="' + col.type + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>',
'</div>'
].join('')).appendTo($item);
}
}
return $item.attr('data-type', 'story').addClass('kanban-item-story');
}
/**
* Render bug item
* @param {Object} item Bug item object
* @param {JQuery} $item Kanban item element
* @param {Object} col Column object
* @returns {JQuery} $item Kanban item element
*/
function renderBugItem(item, $item, col)
{
var scaleSize = window.kanbanScaleSize;
if(+$item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize);
if(scaleSize <= 3)
{
var $title = $item.find('.title');
if(!$title.length)
{
$title = $('<a class="title iframe" data-width="95%">' + (scaleSize <= 1 ? '<i class="icon icon-bug text-muted"></i> ' : '') + '<span class="text"></span></a>')
.attr('href', $.createLink('bug', 'view', 'bugID=' + item.id, '', true)).attr('data-toggle', 'modal').attr('data-width', '80%');
$title.appendTo($item);
}
$title.attr('title', item.title).find('.text').text(item.title);
}
if(scaleSize <= 2)
{
var idHtml = scaleSize <= 1 ? ('<span class="info info-id text-muted">#' + item.id + '</span>') : '';
var severityHtml = scaleSize <= 1 ? ('<span class="info info-severity label-severity" data-severity="' + item.severity + '" title="' + item.severity + '"></span>') : '';
var priHtml = '<span class="info info-pri label-pri label-pri-' + item.pri + '" title="' + item.pri + '">' + item.pri + '</span>';
var avatarHtml = renderUserAvatar(item.assignedTo, 'bug', item.id);
var $infos = $item.find('.infos');
if(!$infos.length) $infos = $('<div class="infos"></div>');
$infos.html([idHtml, severityHtml, priHtml].join(''));
if(item.deadline && scaleSize <= 1) $infos.append(renderDeadline(item.deadline));
$infos[scaleSize <= 1 ? 'append' : 'prepend'](avatarHtml);
if(scaleSize <= 1) $infos.appendTo($item);
else if(scaleSize === 2) $infos.prependTo($item);
else $infos.prependTo($item.find('.title'));
}
else if(scaleSize === 4)
{
$item.html(renderUserAvatar(item.assignedTo, 'bug', item.id, 'md'));
}
if(scaleSize <= 1)
{
var $actions = $item.find('.actions');
if(!$actions.length && item.menus && item.menus.length)
{
$actions = $([
'<div class="actions">',
'<a data-contextmenu="bug" data-col="' + col.type + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>',
'</div>'
].join('')).appendTo($item);
}
}
return $item.attr('data-type', 'bug').addClass('kanban-item-bug');
}
/**
* Render task item
* @param {Object} item Task item object
* @param {JQuery} $item Kanban item element
* @param {Object} col Column object
* @returns {JQuery} $item Kanban item element
*/
function renderTaskItem(item, $item, col)
{
var scaleSize = window.kanbanScaleSize;
if(+$item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize);
if(scaleSize <= 3)
{
var $title = $item.find('.title');
if(!$title.length)
{
$title = $('<a class="title iframe" data-width="95%">' + (scaleSize <= 1 ? '<i class="icon icon-checked text-muted"></i> ' : '') + '<span class="text"></span></a>')
.attr('href', $.createLink('task', 'view', 'taskID=' + item.id, '', true)).attr('data-toggle', 'modal').attr('data-width', '80%');
$title.appendTo($item);
}
$title.attr('title', item.name).find('.text').text(item.name);
}
if(scaleSize <= 2)
{
var idHtml = scaleSize <= 1 ? ('<span class="info info-id text-muted">#' + item.id + '</span>') : '';
var priHtml = '<span class="info info-pri label-pri label-pri-' + item.pri + '" title="' + item.pri + '">' + item.pri + '</span>';
var hoursHtml = (item.estimate && scaleSize <= 1) ? ('<span class="info info-estimate text-muted">' + item.estimate + 'h</span>') : '';
var avatarHtml = renderUserAvatar(item.assignedTo, 'task', item.id);
var $infos = $item.find('.infos');
if(!$infos.length) $infos = $('<div class="infos"></div>');
$infos.html([idHtml, priHtml, hoursHtml].join(''));
if(item.deadline && scaleSize <= 1) $infos.append(renderDeadline(item.deadline));
$infos[scaleSize <= 1 ? 'append' : 'prepend'](avatarHtml);
if(scaleSize <= 1) $infos.appendTo($item);
else if(scaleSize === 2) $infos.prependTo($item);
else $infos.prependTo($item.find('.title'));
}
else if(scaleSize === 4)
{
$item.html(renderUserAvatar(item.assignedTo, 'task', item.id, 'md'));
}
if(scaleSize <= 1)
{
var $actions = $item.find('.actions');
if(!$actions.length && item.menus && item.menus.length)
{
$actions = $([
'<div class="actions">',
'<a data-contextmenu="task" data-col="' + col.type + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>',
'</div>'
].join('')).appendTo($item);
}
}
$item.attr('data-type', 'task').addClass('kanban-item-task');
return $item;
}
/* Add column renderer */
addColumnRenderer('story', renderStoryItem);
addColumnRenderer('bug', renderBugItem);
addColumnRenderer('task', renderTaskItem);
/**
* Render items count of a column.
*/
@@ -196,7 +493,7 @@ function renderHeaderCol($column, column, $header, kanbanData)
/* Render group header. */
var privs = kanbanData.actions;
var columnPrivs = kanbanData.columns[0].actions;
var $actions = $('<div class="actions" />');
var $actions = $column.children('.actions');
if(privs.includes('sortGroup'))
{
@@ -213,14 +510,10 @@ function renderHeaderCol($column, column, $header, kanbanData)
}
}
var regionID = $column.closest('.kanban').data('id');
var groupID = $column.closest('.kanban-board').data('id');
var laneID = column.$kanbanData.lanes[0].id ? column.$kanbanData.lanes[0].id : 0;
var columnID = $column.closest('.kanban-col').data('id');
var printMoreBtn = (columnPrivs.includes('setColumn') || columnPrivs.includes('setWIP') || columnPrivs.includes('deleteColumn'));
var printMoreBtn = (columnPrivs.includes('setColumn') || columnPrivs.includes('setWIP'));
/* Render more menu. */
if((column.type == 'backlog' && hasStoryButton) || (column.type == 'wait' && hasTaskButton) || (column.type == 'unconfirmed' && hasBugButton))
if(((column.type == 'backlog' && hasStoryButton) || (column.type == 'wait' && hasTaskButton) || (column.type == 'unconfirmed' && hasBugButton)) && $actions.children('.text-primary').length == 0)
{
$actions.append([
'<a data-contextmenu="columnCreate" data-type="' + column.type + '" data-kanban="' + kanban.id + '" data-parent="' + (column.parentType || '') + '" class="text-primary">',
@@ -228,11 +521,10 @@ function renderHeaderCol($column, column, $header, kanbanData)
'</a>'
].join(''));
}
if(printMoreBtn)
if(printMoreBtn && $actions.children('.btn').length == 0)
{
$actions.append(' <button class="btn btn-link action" title="' + kanbanLang.moreAction + '" data-contextmenu="column" data-column="' + column.id + '"><i class="icon icon-ellipsis-v"></i></button>');
}
$actions.appendTo($column);
}
/**
@@ -266,28 +558,6 @@ function renderLaneName($lane, lane, $kanban, columns, kanban)
}
}
/**
* Adjust add button postion in column
*/
function adjustAddBtnPosition($kanban)
{
if(!$kanban)
{
$('.kanban').children('.kanban-board').each(function()
{
adjustAddBtnPosition($(this));
});
return;
}
$kanban.find('.kanban-lane-col:not([data-type="EMPTY"])').each(function()
{
var $col = $(this);
var items = $col.children('.kanban-lane-items')[0];
$col.toggleClass('has-scrollbar', items.scrollHeight > items.clientHeight);
});
}
/**
* Handle kanban action
*/
@@ -316,94 +586,6 @@ function processMinusBtn()
}
}
/**
* The function for rendering kanban item
*/
function renderKanbanItem(item, $item)
{
var $title = $item.children('.title');
var privs = item.actions;
var printMoreBtn = (privs.includes('editCard') || privs.includes('archiveCard') || privs.includes('copyCard') || privs.includes('deleteCard') || privs.includes('moveCard') || privs.includes('setCardColor'));
if(!$title.length)
{
if(privs.includes('viewCard')) $title = $('<a class="title iframe" data-toggle="modal" data-width="80%"></a>').appendTo($item).attr('href', createLink('kanban', 'viewCard', 'cardID=' + item.id, '', true));
if(!privs.includes('viewCard')) $title = $('<p class="title"></p>').appendTo($item);
}
$title.text(item.name).attr('title', item.name);
if(printMoreBtn)
{
$(
[
'<div class="actions" title="' + kanbanLang.more + '">',
'<a data-contextmenu="card" data-id="' + item.id + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>',
'</div>'
].join('')).appendTo($item);
}
var $info = $item.children('.info');
if(!$info.length) $info = $(
[
'<div class="info">',
'<span class="pri"></span>',
'<span class="estimate label label-light"></span>',
'<span class="time label label-light"></span>',
'<div class="user"></div>',
'</div>'
].join('')).appendTo($item);
$item.data('card', item);
$info.children('.estimate').text(item.estimate + kanbancardLang.lblHour);
if(item.estimate == 0) $info.children('.estimate').hide();
$info.children('.pri')
.attr('class', 'pri label-pri label-pri-' + item.pri)
.text(item.pri);
$item.css('background-color', item.color);
$item.toggleClass('has-color', item.color != '#fff' && item.color != '');
$item.find('.info > .label-light').css('background-color', item.color);
var $time = $info.children('.time');
if(item.end == '0000-00-00' && item.begin == '0000-00-00')
{
$time.hide();
}
else
{
var today = new Date();
var begin = $.zui.createDate(item.begin);
var end = $.zui.createDate(item.end);
var needRemind = (begin.toLocaleDateString() == today.toLocaleDateString() || end.toLocaleDateString() == today.toLocaleDateString());
if(item.end == '0000-00-00' && item.begin != '0000-00-00')
{
$time.text($.zui.formatDate(begin, 'MM/dd') + ' ' + kanbancardLang.beginAB).attr('title', $.zui.formatDate(begin, 'yyyy/MM/dd') + ' ' +kanbancardLang.beginAB).show();
}
else if(item.begin == '0000-00-00' && item.end != '0000-00-00')
{
$time.text($.zui.formatDate(end, 'MM/dd') + ' ' + kanbancardLang.deadlineAB).attr('title', $.zui.formatDate(end, 'yyyy/MM/dd') + ' ' + kanbancardLang.deadlineAB).show();
}
else if(item.begin != '0000-00-00' && item.end != '0000-00-00')
{
$time.text($.zui.formatDate(begin, 'MM/dd') + ' ' + kanbancardLang.to + ' ' + $.zui.formatDate(end, 'MM/dd')).attr('title', $.zui.formatDate(begin, 'yyyy/MM/dd') + kanbancardLang.to + $.zui.formatDate(end, 'yyyy/MM/dd')).show();
}
if(!$item.hasClass('has-color') && needRemind) $time.css('background-color', 'rgba(210, 50, 61, 0.3)');
if($item.hasClass('has-color') && needRemind) $time.css('background-color', 'rgba(255, 255, 255, 0.3)');
if(!needRemind) $time.css('background-color', 'rgba(0, 0, 0, 0.15)');
}
/* Display avatars of assignedTo. */
var assignedTo = item.assignedTo.split(',');
var $user = $info.children('.user');
var title = [];
for(i = 0; i < assignedTo.length; i++) title.push(users[assignedTo[i]]);
$user.html(renderUsersAvatar(assignedTo, item.id)).attr('title', title);
}
/* Define menu creators */
window.menuCreators =
{
@@ -429,9 +611,9 @@ function initKanban($kanban)
maxColWidth: 300,
createColumnText: kanbanLang.createColumn,
addItemText: '',
itemRender: renderKanbanItem,
cardHeight: getCardHeight(),
cardsPerRow: window.kanbanScaleSize,
onAction: handleKanbanAction,
onRenderKanban: adjustAddBtnPosition,
onRenderLaneName: renderLaneName,
onRenderHeaderCol: renderHeaderCol,
onRenderCount: renderCount,
@@ -511,13 +693,6 @@ $(function()
$.zui.ContextMenu.show(items, items.$options || {event: event});
});
/* Adjust the add button position on window resize */
$(window).on('resize', function(a)
{
adjustAddBtnPosition();
});
/* Hide contextmenu when page scroll */
$(window).on('scroll', function()
{
+100 -99
View File
@@ -10,105 +10,106 @@
* @link http://www.zentao.net
*/
/* Fields. */
$lang->execution->allExecutions = 'All ' . $lang->execution->common . 's';
$lang->execution->allExecutionAB = 'Execution List';
$lang->execution->id = $lang->executionCommon . ' ID';
$lang->execution->type = $lang->executionCommon . 'Type';
$lang->execution->name = $lang->executionCommon . 'Name';
$lang->execution->code = $lang->executionCommon . 'Code';
$lang->execution->projectName = 'Project';
$lang->execution->execName = 'Execution Name';
$lang->execution->execCode = 'Execution Code';
$lang->execution->execType = 'Execution Type';
$lang->execution->stage = 'Stage';
$lang->execution->pri = 'Priority';
$lang->execution->openedBy = 'OpenedBy';
$lang->execution->openedDate = 'OpenedDate';
$lang->execution->closedBy = 'ClosedBy';
$lang->execution->closedDate = 'ClosedDate';
$lang->execution->canceledBy = 'CanceledBy';
$lang->execution->canceledDate = 'CanceledDate';
$lang->execution->begin = 'Planned Begin';
$lang->execution->end = 'Planned End';
$lang->execution->dateRange = 'Duration';
$lang->execution->realBeganAB = 'Actual Begin';
$lang->execution->realEndAB = 'Actual End';
$lang->execution->realBegan = 'Actual Begin';
$lang->execution->realEnd = 'Actual End';
$lang->execution->to = 'To';
$lang->execution->days = ' Days';
$lang->execution->day = ' Days';
$lang->execution->workHour = ' Hours';
$lang->execution->workHourUnit = 'H';
$lang->execution->totalHours = ' Hours';
$lang->execution->totalDays = ' Days';
$lang->execution->status = $lang->executionCommon . 'Status';
$lang->execution->execStatus = 'Status';
$lang->execution->subStatus = 'Sub Status';
$lang->execution->desc = $lang->executionCommon . 'Description';
$lang->execution->execDesc = 'Description';
$lang->execution->owner = 'Owner';
$lang->execution->PO = "{$lang->executionCommon} Owner";
$lang->execution->PM = "{$lang->executionCommon} Manager";
$lang->execution->execPM = "Execution Manager";
$lang->execution->QD = 'Test Manager';
$lang->execution->RD = 'Release Manager';
$lang->execution->release = 'Release';
$lang->execution->acl = 'Access Control';
$lang->execution->teamname = 'Team Name';
$lang->execution->updateOrder = 'Rank';
$lang->execution->order = "Rank {$lang->executionCommon}";
$lang->execution->orderAB = "Rank";
$lang->execution->products = "Link {$lang->productCommon}";
$lang->execution->whitelist = 'Whitelist';
$lang->execution->addWhitelist = 'Add Whitelist';
$lang->execution->unbindWhitelist = 'Remove Whitelist';
$lang->execution->totalEstimate = 'Estimates';
$lang->execution->totalConsumed = 'Cost';
$lang->execution->totalLeft = 'Left';
$lang->execution->progress = ' Progress';
$lang->execution->hours = 'Estimates: %s, Cost: %s, Left: %s.';
$lang->execution->viewBug = 'Bugs';
$lang->execution->noProduct = "No {$lang->productCommon} yet.";
$lang->execution->createStory = "Create Story";
$lang->execution->storyTitle = "Story Name";
$lang->execution->all = "All {$lang->executionCommon}s";
$lang->execution->undone = 'Unfinished ';
$lang->execution->unclosed = 'Unclosed';
$lang->execution->typeDesc = "OPS {$lang->executionCommon} has no {$lang->SRCommon}, Bug, Build, or Test features.";
$lang->execution->mine = 'Mine: ';
$lang->execution->involved = 'Mine';
$lang->execution->other = 'Others';
$lang->execution->deleted = 'Deleted';
$lang->execution->delayed = 'Delayed';
$lang->execution->product = $lang->execution->products;
$lang->execution->readjustTime = "Adjust {$lang->executionCommon} Begin and End";
$lang->execution->readjustTask = 'Adjust Task Begin and End';
$lang->execution->effort = 'Effort';
$lang->execution->storyEstimate = 'Story Estimate';
$lang->execution->newEstimate = 'New Estimate';
$lang->execution->reestimate = 'Reestimate';
$lang->execution->selectRound = 'Select Round';
$lang->execution->average = 'Average';
$lang->execution->relatedMember = 'Team';
$lang->execution->watermark = 'Exported by ZenTao';
$lang->execution->burnXUnit = '(Date)';
$lang->execution->burnYUnit = '(Hours)';
$lang->execution->waitTasks = 'Waiting Tasks';
$lang->execution->viewByUser = 'By User';
$lang->execution->oneProduct = "Only one stage can be linked {$lang->productCommon}";
$lang->execution->noLinkProduct = "Stage not linked {$lang->productCommon}";
$lang->execution->recent = 'Recent visits: ';
$lang->execution->copyNoExecution = 'There are no ' . $lang->executionCommon . 'available to copy.';
$lang->execution->noTeam = 'No team members at the moment';
$lang->execution->or = ' or ';
$lang->execution->selectProject = 'Please select project';
$lang->execution->unfoldClosed = 'Unfold Closed';
$lang->execution->editName = 'Edit Name';
$lang->execution->setWIP = 'WIP Settings';
$lang->execution->sortColumn = 'Kanban Card Sorting';
$lang->execution->batchCreateStroy = "Batch create {$lang->SRCommon}";
$lang->execution->batchCreateTask = 'Batch create task';
$lang->execution->allExecutions = 'All ' . $lang->execution->common . 's';
$lang->execution->allExecutionAB = 'Execution List';
$lang->execution->id = $lang->executionCommon . ' ID';
$lang->execution->type = $lang->executionCommon . 'Type';
$lang->execution->name = $lang->executionCommon . 'Name';
$lang->execution->code = $lang->executionCommon . 'Code';
$lang->execution->projectName = 'Project';
$lang->execution->execName = 'Execution Name';
$lang->execution->execCode = 'Execution Code';
$lang->execution->execType = 'Execution Type';
$lang->execution->stage = 'Stage';
$lang->execution->pri = 'Priority';
$lang->execution->openedBy = 'OpenedBy';
$lang->execution->openedDate = 'OpenedDate';
$lang->execution->closedBy = 'ClosedBy';
$lang->execution->closedDate = 'ClosedDate';
$lang->execution->canceledBy = 'CanceledBy';
$lang->execution->canceledDate = 'CanceledDate';
$lang->execution->begin = 'Planned Begin';
$lang->execution->end = 'Planned End';
$lang->execution->dateRange = 'Duration';
$lang->execution->realBeganAB = 'Actual Begin';
$lang->execution->realEndAB = 'Actual End';
$lang->execution->realBegan = 'Actual Begin';
$lang->execution->realEnd = 'Actual End';
$lang->execution->to = 'To';
$lang->execution->days = ' Days';
$lang->execution->day = ' Days';
$lang->execution->workHour = ' Hours';
$lang->execution->workHourUnit = 'H';
$lang->execution->totalHours = ' Hours';
$lang->execution->totalDays = ' Days';
$lang->execution->status = $lang->executionCommon . 'Status';
$lang->execution->execStatus = 'Status';
$lang->execution->subStatus = 'Sub Status';
$lang->execution->desc = $lang->executionCommon . 'Description';
$lang->execution->execDesc = 'Description';
$lang->execution->owner = 'Owner';
$lang->execution->PO = "{$lang->executionCommon} Owner";
$lang->execution->PM = "{$lang->executionCommon} Manager";
$lang->execution->execPM = "Execution Manager";
$lang->execution->QD = 'Test Manager';
$lang->execution->RD = 'Release Manager';
$lang->execution->release = 'Release';
$lang->execution->acl = 'Access Control';
$lang->execution->teamname = 'Team Name';
$lang->execution->updateOrder = 'Rank';
$lang->execution->order = "Rank {$lang->executionCommon}";
$lang->execution->orderAB = "Rank";
$lang->execution->products = "Link {$lang->productCommon}";
$lang->execution->whitelist = 'Whitelist';
$lang->execution->addWhitelist = 'Add Whitelist';
$lang->execution->unbindWhitelist = 'Remove Whitelist';
$lang->execution->totalEstimate = 'Estimates';
$lang->execution->totalConsumed = 'Cost';
$lang->execution->totalLeft = 'Left';
$lang->execution->progress = ' Progress';
$lang->execution->hours = 'Estimates: %s, Cost: %s, Left: %s.';
$lang->execution->viewBug = 'Bugs';
$lang->execution->noProduct = "No {$lang->productCommon} yet.";
$lang->execution->createStory = "Create Story";
$lang->execution->storyTitle = "Story Name";
$lang->execution->all = "All {$lang->executionCommon}s";
$lang->execution->undone = 'Unfinished ';
$lang->execution->unclosed = 'Unclosed';
$lang->execution->typeDesc = "OPS {$lang->executionCommon} has no {$lang->SRCommon}, Bug, Build, or Test features.";
$lang->execution->mine = 'Mine: ';
$lang->execution->involved = 'Mine';
$lang->execution->other = 'Others';
$lang->execution->deleted = 'Deleted';
$lang->execution->delayed = 'Delayed';
$lang->execution->product = $lang->execution->products;
$lang->execution->readjustTime = "Adjust {$lang->executionCommon} Begin and End";
$lang->execution->readjustTask = 'Adjust Task Begin and End';
$lang->execution->effort = 'Effort';
$lang->execution->storyEstimate = 'Story Estimate';
$lang->execution->newEstimate = 'New Estimate';
$lang->execution->reestimate = 'Reestimate';
$lang->execution->selectRound = 'Select Round';
$lang->execution->average = 'Average';
$lang->execution->relatedMember = 'Team';
$lang->execution->watermark = 'Exported by ZenTao';
$lang->execution->burnXUnit = '(Date)';
$lang->execution->burnYUnit = '(Hours)';
$lang->execution->waitTasks = 'Waiting Tasks';
$lang->execution->viewByUser = 'By User';
$lang->execution->oneProduct = "Only one stage can be linked {$lang->productCommon}";
$lang->execution->noLinkProduct = "Stage not linked {$lang->productCommon}";
$lang->execution->recent = 'Recent visits: ';
$lang->execution->copyNoExecution = 'There are no ' . $lang->executionCommon . 'available to copy.';
$lang->execution->noTeam = 'No team members at the moment';
$lang->execution->or = ' or ';
$lang->execution->selectProject = 'Please select project';
$lang->execution->unfoldClosed = 'Unfold Closed';
$lang->execution->editName = 'Edit Name';
$lang->execution->setWIP = 'WIP Settings';
$lang->execution->sortColumn = 'Kanban Card Sorting';
$lang->execution->batchCreateStroy = "Batch create {$lang->SRCommon}";
$lang->execution->batchCreateTask = 'Batch create task';
$lang->execution->kanbanNoLinkProduct = "Kanban not linked {$lang->productCommon}";
/* Fields of zt_team. */
$lang->execution->root = 'Root';
+100 -99
View File
@@ -10,105 +10,106 @@
* @link http://www.zentao.net
*/
/* 字段列表。*/
$lang->execution->allExecutions = '所有' . $lang->execution->common;
$lang->execution->allExecutionAB = "{$lang->execution->common}列表";
$lang->execution->id = $lang->executionCommon . '编号';
$lang->execution->type = $lang->executionCommon . '类型';
$lang->execution->name = $lang->executionCommon . '名称';
$lang->execution->code = $lang->executionCommon . '代号';
$lang->execution->projectName = '所属项目';
$lang->execution->execName = "{$lang->execution->common}名称";
$lang->execution->execCode = "{$lang->execution->common}代号";
$lang->execution->execType = "{$lang->execution->common}类型";
$lang->execution->stage = '阶段';
$lang->execution->pri = '优先级';
$lang->execution->openedBy = '由谁创建';
$lang->execution->openedDate = '创建日期';
$lang->execution->closedBy = '由谁关闭';
$lang->execution->closedDate = '关闭日期';
$lang->execution->canceledBy = '由谁取消';
$lang->execution->canceledDate = '取消日期';
$lang->execution->begin = '计划开始';
$lang->execution->end = '计划完成';
$lang->execution->dateRange = '起始日期';
$lang->execution->realBeganAB = '实际开始';
$lang->execution->realEndAB = '实际完成';
$lang->execution->realBegan = '实际开始日期';
$lang->execution->realEnd = '实际完成日期';
$lang->execution->to = '至';
$lang->execution->days = '可用工作日';
$lang->execution->day = '天';
$lang->execution->workHour = '工时';
$lang->execution->workHourUnit = 'h';
$lang->execution->totalHours = '可用工时';
$lang->execution->totalDays = '可用工日';
$lang->execution->status = $lang->executionCommon . '状态';
$lang->execution->execStatus = "{$lang->execution->common}状态";
$lang->execution->subStatus = '子状态';
$lang->execution->desc = $lang->executionCommon . '描述';
$lang->execution->execDesc = "{$lang->execution->common}描述";
$lang->execution->owner = '负责人';
$lang->execution->PO = $lang->productCommon . '负责人';
$lang->execution->PM = $lang->executionCommon . '负责人';
$lang->execution->execPM = "{$lang->execution->common}负责人";
$lang->execution->QD = '测试负责人';
$lang->execution->RD = '发布负责人';
$lang->execution->release = '发布';
$lang->execution->acl = '访问控制';
$lang->execution->teamname = '团队名称';
$lang->execution->updateOrder = '排序';
$lang->execution->order = $lang->executionCommon . '排序';
$lang->execution->orderAB = '排序';
$lang->execution->products = '相关' . $lang->productCommon;
$lang->execution->whitelist = '白名单';
$lang->execution->addWhitelist = '添加白名单';
$lang->execution->unbindWhitelist = '删除白名单';
$lang->execution->totalEstimate = '预计';
$lang->execution->totalConsumed = '消耗';
$lang->execution->totalLeft = '剩余';
$lang->execution->progress = '进度';
$lang->execution->hours = '预计 %s 消耗 %s 剩余 %s';
$lang->execution->viewBug = '查看bug';
$lang->execution->noProduct = "无{$lang->executionCommon}";
$lang->execution->createStory = "提{$lang->SRCommon}";
$lang->execution->storyTitle = "{$lang->SRCommon}名称";
$lang->execution->all = '所有';
$lang->execution->undone = '未完成';
$lang->execution->unclosed = '未关闭';
$lang->execution->typeDesc = "运维{$lang->executionCommon}没有{$lang->SRCommon}、bug、版本、测试功能。";
$lang->execution->mine = '我负责:';
$lang->execution->involved = '我参与';
$lang->execution->other = '其他';
$lang->execution->deleted = '已删除';
$lang->execution->delayed = '已延期';
$lang->execution->product = $lang->execution->products;
$lang->execution->readjustTime = "调整{$lang->executionCommon}起止时间";
$lang->execution->readjustTask = '顺延任务的起止时间';
$lang->execution->effort = '日志';
$lang->execution->storyEstimate = '需求估算';
$lang->execution->newEstimate = '新一轮估算';
$lang->execution->reestimate = '重新估算';
$lang->execution->selectRound = '选择轮次';
$lang->execution->average = '平均值';
$lang->execution->relatedMember = '相关成员';
$lang->execution->watermark = '由禅道导出';
$lang->execution->burnXUnit = '(日期)';
$lang->execution->burnYUnit = '(工时)';
$lang->execution->waitTasks = '待处理';
$lang->execution->viewByUser = '按用户查看';
$lang->execution->oneProduct = "阶段只能关联一个{$lang->productCommon}";
$lang->execution->noLinkProduct = "阶段没有关联{$lang->productCommon}";
$lang->execution->recent = '近期访问:';
$lang->execution->copyNoExecution = '没有可用的' . $lang->executionCommon . '来复制';
$lang->execution->noTeam = '暂时没有团队成员';
$lang->execution->or = '或';
$lang->execution->selectProject = '请选择项目';
$lang->execution->unfoldClosed = '展开已结束';
$lang->execution->editName = '编辑名称';
$lang->execution->setWIP = '在制品数量设置(WIP)';
$lang->execution->sortColumn = '看板列卡片排序';
$lang->execution->batchCreateStroy = "批量新建{$lang->SRCommon}";
$lang->execution->batchCreateTask = '批量建任务';
$lang->execution->allExecutions = '所有' . $lang->execution->common;
$lang->execution->allExecutionAB = "{$lang->execution->common}列表";
$lang->execution->id = $lang->executionCommon . '编号';
$lang->execution->type = $lang->executionCommon . '类型';
$lang->execution->name = $lang->executionCommon . '名称';
$lang->execution->code = $lang->executionCommon . '代号';
$lang->execution->projectName = '所属项目';
$lang->execution->execName = "{$lang->execution->common}名称";
$lang->execution->execCode = "{$lang->execution->common}代号";
$lang->execution->execType = "{$lang->execution->common}类型";
$lang->execution->stage = '阶段';
$lang->execution->pri = '优先级';
$lang->execution->openedBy = '由谁创建';
$lang->execution->openedDate = '创建日期';
$lang->execution->closedBy = '由谁关闭';
$lang->execution->closedDate = '关闭日期';
$lang->execution->canceledBy = '由谁取消';
$lang->execution->canceledDate = '取消日期';
$lang->execution->begin = '计划开始';
$lang->execution->end = '计划完成';
$lang->execution->dateRange = '起始日期';
$lang->execution->realBeganAB = '实际开始';
$lang->execution->realEndAB = '实际完成';
$lang->execution->realBegan = '实际开始日期';
$lang->execution->realEnd = '实际完成日期';
$lang->execution->to = '至';
$lang->execution->days = '可用工作日';
$lang->execution->day = '天';
$lang->execution->workHour = '工时';
$lang->execution->workHourUnit = 'h';
$lang->execution->totalHours = '可用工时';
$lang->execution->totalDays = '可用工日';
$lang->execution->status = $lang->executionCommon . '状态';
$lang->execution->execStatus = "{$lang->execution->common}状态";
$lang->execution->subStatus = '子状态';
$lang->execution->desc = $lang->executionCommon . '描述';
$lang->execution->execDesc = "{$lang->execution->common}描述";
$lang->execution->owner = '负责人';
$lang->execution->PO = $lang->productCommon . '负责人';
$lang->execution->PM = $lang->executionCommon . '负责人';
$lang->execution->execPM = "{$lang->execution->common}负责人";
$lang->execution->QD = '测试负责人';
$lang->execution->RD = '发布负责人';
$lang->execution->release = '发布';
$lang->execution->acl = '访问控制';
$lang->execution->teamname = '团队名称';
$lang->execution->updateOrder = '排序';
$lang->execution->order = $lang->executionCommon . '排序';
$lang->execution->orderAB = '排序';
$lang->execution->products = '相关' . $lang->productCommon;
$lang->execution->whitelist = '白名单';
$lang->execution->addWhitelist = '添加白名单';
$lang->execution->unbindWhitelist = '删除白名单';
$lang->execution->totalEstimate = '预计';
$lang->execution->totalConsumed = '消耗';
$lang->execution->totalLeft = '剩余';
$lang->execution->progress = '进度';
$lang->execution->hours = '预计 %s 消耗 %s 剩余 %s';
$lang->execution->viewBug = '查看bug';
$lang->execution->noProduct = "无{$lang->executionCommon}";
$lang->execution->createStory = "提{$lang->SRCommon}";
$lang->execution->storyTitle = "{$lang->SRCommon}名称";
$lang->execution->all = '所有';
$lang->execution->undone = '未完成';
$lang->execution->unclosed = '未关闭';
$lang->execution->typeDesc = "运维{$lang->executionCommon}没有{$lang->SRCommon}、bug、版本、测试功能。";
$lang->execution->mine = '我负责:';
$lang->execution->involved = '我参与';
$lang->execution->other = '其他';
$lang->execution->deleted = '已删除';
$lang->execution->delayed = '已延期';
$lang->execution->product = $lang->execution->products;
$lang->execution->readjustTime = "调整{$lang->executionCommon}起止时间";
$lang->execution->readjustTask = '顺延任务的起止时间';
$lang->execution->effort = '日志';
$lang->execution->storyEstimate = '需求估算';
$lang->execution->newEstimate = '新一轮估算';
$lang->execution->reestimate = '重新估算';
$lang->execution->selectRound = '选择轮次';
$lang->execution->average = '平均值';
$lang->execution->relatedMember = '相关成员';
$lang->execution->watermark = '由禅道导出';
$lang->execution->burnXUnit = '(日期)';
$lang->execution->burnYUnit = '(工时)';
$lang->execution->waitTasks = '待处理';
$lang->execution->viewByUser = '按用户查看';
$lang->execution->oneProduct = "阶段只能关联一个{$lang->productCommon}";
$lang->execution->noLinkProduct = "阶段没有关联{$lang->productCommon}";
$lang->execution->recent = '近期访问:';
$lang->execution->copyNoExecution = '没有可用的' . $lang->executionCommon . '来复制';
$lang->execution->noTeam = '暂时没有团队成员';
$lang->execution->or = '或';
$lang->execution->selectProject = '请选择项目';
$lang->execution->unfoldClosed = '展开已结束';
$lang->execution->editName = '编辑名称';
$lang->execution->setWIP = '在制品数量设置(WIP)';
$lang->execution->sortColumn = '看板列卡片排序';
$lang->execution->batchCreateStroy = "批量新建{$lang->SRCommon}";
$lang->execution->batchCreateTask = '批量建任务';
$lang->execution->kanbanNoLinkProduct = "看板没有关联{$lang->productCommon}";
/* Fields of zt_team. */
$lang->execution->root = '源ID';
+6
View File
@@ -312,6 +312,12 @@ class executionModel extends model
return false;
}
if($type == 'kanban' and empty($this->post->products[0]))
{
dao::$errors['message'][] = $this->lang->execution->kanbanNoLinkProduct;
return false;
}
$this->config->execution->create->requiredFields .= ',project';
}
+11 -2
View File
@@ -62,6 +62,17 @@ js::set('priv',
'canBatchCreateStory' => $canBatchCreateStory,
'canLinkStory' => $canLinkStory,
'canLinkStoryByPlane' => $canLinkStoryByPlane,
'canAssignTask' => common::hasPriv('task', 'assignto'),
'canAssignStory' => common::hasPriv('story', 'assignto'),
'canFinishTask' => common::hasPriv('task', 'finish'),
'canPauseTask' => common::hasPriv('task', 'pause'),
'canCancelTask' => common::hasPriv('task', 'cancel'),
'canCloseTask' => common::hasPriv('task', 'close'),
'canActivateTask' => common::hasPriv('task', 'activate'),
'canStartTask' => common::hasPriv('task', 'start'),
'canAssignBug' => common::hasPriv('bug', 'assignto'),
'canConfirmBug' => common::hasPriv('bug', 'confirmBug'),
'canActivateBug' => common::hasPriv('bug', 'activate')
)
);
js::set('hasStoryButton', $hasStoryButton);
@@ -119,8 +130,6 @@ js::set('hasTaskButton', $hasTaskButton);
$actions .= "</ul>";
}
$actions .= "</div>";
echo $actions;
?>
</div>
+67 -12
View File
@@ -655,7 +655,12 @@ class kanbanModel extends model
$lanes = zget($laneGroup, $group->id, array());
if(!$lanes) continue;
foreach($lanes as $lane) $lane->items = array();
foreach($lanes as $lane)
{
$this->refreshCards($lane);
$lane->items = isset($cardGroup[$lane->id]) ? $cardGroup[$lane->id] : array();
$lane->defaultCardType = $lane->type;
}
$group->columns = zget($columnGroup, $group->id, array());
$group->lanes = $lanes;
@@ -919,12 +924,60 @@ class kanbanModel extends model
*/
public function getCardGroupByExecution($executionID, $browseType = 'all', $orderBy = 'id_asc')
{
$cards = array();
if($browseType == 'all' or $browseType == 'story') $cards['story'] = $this->loadModel('story')->getExecutionStories($executionID, 0, 0, $orderBy);
if($browseType == 'all' or $browseType == 'bug') $cards['bug'] = $this->loadModel('bug')->getExecutionBugs($executionID, 0, 0, '', 0, $orderBy);
if($browseType == 'all' or $browseType == 'task') $cards['task'] = $this->loadModel('execution')->getKanbanTasks($executionID, $orderBy);
$cards = $this->dao->select('t1.*, t2.type as columnType')
->from(TABLE_KANBANCELL)->alias('t1')
->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.column=t2.id')
->where('t1.kanban')->eq($executionID)
->beginIF($browseType != 'all')->andWhere('t1.type')->eq($browseType)->fi()
->orderby($orderBy)
->fetchgroup('lane', 'column');
return $cards;
/* Get group objects. */
if($browseType == 'all' or $browseType == 'story') $objectGroup['story'] = $this->loadModel('story')->getExecutionStories($executionID);
if($browseType == 'all' or $browseType == 'bug') $objectGroup['bug'] = $this->loadModel('bug')->getExecutionBugs($executionID);
if($browseType == 'all' or $browseType == 'task') $objectGroup['task'] = $this->loadModel('execution')->getKanbanTasks($executionID, "id");
$cardGroup = array();
foreach($cards as $laneID => $cells)
{
foreach($cells as $columnID => $cell)
{
$cardIdList = array_filter(explode(',', $cell->cards));
$cardOrder = 1;
foreach($cardIdList as $cardID)
{
$cardData = array();
$objects = zget($objectGroup, $cell->type, array());
$object = zget($objects, $cardID, array());
if(empty($object)) continue;
$cardData['id'] = $object->id;
$cardData['order'] = $cardOrder++;
$cardData['pri'] = $object->pri ? $object->pri : '';
$cardData['estimate'] = $cell->type == 'bug' ? '' : $object->estimate;
$cardData['assignedTo'] = $object->assignedTo;
$cardData['deadline'] = $cell->type == 'story' ? '' : $object->deadline;
$cardData['severity'] = $cell->type == 'bug' ? $object->severity : '';
$cardData['acl'] = 'open';
$cardData['lane'] = $laneID;
$cardData['column'] = $cell->column;
if($cell->type == 'task')
{
$cardData['name'] = $object->name;
}
else
{
$cardData['title'] = $object->title;
}
$cardGroup[$laneID][$cell->columnType][] = $cardData;
}
}
}
return $cardGroup;
}
/**
@@ -1624,10 +1677,10 @@ class kanbanModel extends model
/**
* Add kanban cell for new lane.
*
* @param int $kanbanID
* @param int $laneID
* @param int $colID
*
* @param int $kanbanID
* @param int $laneID
* @param int $colID
* @param string $type story|task|bug|card
* @access public
* @return void
@@ -1733,7 +1786,7 @@ class kanbanModel extends model
$this->dao->insert(TABLE_KANBANLANE)->data($lane)->autoCheck()->exec();
if(dao::isError()) return false;
$this->createRDColumn($regionID, $groupID, $this->dao->lastInsertId(), $type);
$this->createRDColumn($regionID, $groupID, $this->dao->lastInsertId(), $type, $executionID);
}
}
@@ -1748,7 +1801,7 @@ class kanbanModel extends model
* @access public
* @return bool
*/
public function createRDColumn($regionID, $groupID, $laneID, $laneType)
public function createRDColumn($regionID, $groupID, $laneID, $laneType, $executionID)
{
$devColumnID = $testColumnID = $resolvingColumnID = 0;
if($laneType == 'story') $columnList = $this->lang->kanban->storyColumn;
@@ -1776,6 +1829,8 @@ class kanbanModel extends model
if($type == 'develop') $devColumnID = $this->dao->lastInsertId();
if($type == 'test') $testColumnID = $this->dao->lastInsertId();
if($type == 'resolving') $resolvingColumnID = $this->dao->lastInsertId();
$this->addKanbanCell($executionID, $laneID, $this->dao->lastInsertId(), $laneType);
}
}