Merge branch 'sprint/168' into 'sprint/168_dingna_task_43401'

# Conflicts:
#   module/execution/control.php
#   module/execution/view/kanban.html.php
#   module/kanban/lang/zh-cn.php
#   module/kanban/model.php
This commit is contained in:
李玉春
2021-10-29 05:50:12 +00:00
37 changed files with 870 additions and 677 deletions
+4 -4
View File
@@ -1825,7 +1825,7 @@ class execution extends control
* Kanban.
*
* @param int $executionID
* @param string $type
* @param string $browseType story|bug|task|all
* @param string $orderBy
* @param string $groupBy
* @access public
@@ -1841,11 +1841,11 @@ class execution extends control
/* Compatibility IE8. */
if(strpos($this->server->http_user_agent, 'MSIE 8.0') !== false) header("X-UA-Compatible: IE=EmulateIE7");
$kanbanGroup = $this->loadModel('kanban')->getExecutionKanban($executionID, $type, $groupBy);
$kanbanGroup = $this->loadModel('kanban')->getExecutionKanban($executionID, $browseType);
if(empty($kanbanGroup))
{
$this->kanban->createLanes($executionID, $type, $groupBy);
$kanbanGroup = $this->kanban->getExecutionKanban($executionID);
$this->kanban->createLanes($executionID);
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType);
}
$this->execution->setMenu($executionID);
+17 -1
View File
@@ -1,14 +1,30 @@
#main > .container {max-width: 1960px!important; padding: 0 10px}
.kanban + .kanban {margin-top: 15px}
.kanban-item {position: relative;}
.kanban-item > .title { display: block;white-space: nowrap; overflow: hidden; text-overflow: ellipsis}
.kanban-item > .infos {position: relative; margin-top: 5px}
.kanban-item > .infos > .info + .info {margin-left: 10px}
.kanban-item > .infos > .info + .info {margin-left: 8px}
.kanban-item > .infos > .info-id,
.kanban-item > .infos > .info-deadline,
.kanban-item > .infos > .info-estimate {font-size: 12px; position: relative; top: 2px}
.kanban-item > .infos > .label-pri {min-width: 16px; line-height: 14px; height: 16px; padding: 0}
.kanban-item > .infos > .label-severity {transform: scale(.75)}
.kanban-item > .infos > .avatar {position: absolute; right: 0; top: 0}
.kanban-item > .actions {position: absolute; top: 4px; right: 4px; opacity: 0;}
.kanban-item:hover > .actions {opacity: 1;}
.kanban-item > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.kanban-item > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.kanban-header-col > .actions {padding-top: 22px}
.kanban-header-parent-col .kanban-header-col > .actions {padding-top: 6px; padding-right: 2px;}
.kanban-header-col > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.kanban-header-col > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.kanban-lane-name > .actions {position: absolute; top: 0; left: 0; right: 0; opacity: 0;}
.kanban-lane-name:hover > .actions {opacity: 1;}
.kanban-lane-name > .actions > a {display: block; width: 20px; height: 20px; line-height: 20px; text-align: center; opacity: .7; color: #fff;}
.kanban-lane-name > .actions > a:hover {background-color: rgba(0,0,0,.2); opacity: 1;}
.kanban-affixed .kanban-header-col > .title > .text,
.kanban-affixed .kanban-header-col > .title > .count {color: #fff!important;}
+312 -368
View File
@@ -1,371 +1,45 @@
/**
* Get story demo kanban data
* 获取“软件需求”看板演示数据
* @returns {Object} Kanban data
* @todo 该方法作为演示,应该在最终版本中移除
*/
function getStoryKanbanDemoData()
function changeView(view)
{
/* Define kanban columns 定义看板列 */
var columns =
[
/* 看板列定义数据结构:
id: 列 ID(确保在页面上唯一),
type: 类型,
name: 显示的名称,
color: 列名称颜色,
parentType: 所属的父级列类型
asParent: 是否作为父级列
cardType: 看板卡片类型,例如:'story'(需求)
limit: 最大数目,该列上允许展示的卡片最大数目,如果为 0 表示无限制 */
{id: 'story-blacklog', type: 'blacklog', name: 'Blacklog', color: '', limit: 0},
{id: 'story-ready', type: 'ready', name: '准备好', color: '', limit: 4},
{id: 'story-dev', type: 'dev', name: '开发', color: '', limit: 4, asParent: true},
{id: 'story-dev-doing', type: 'dev-doing', name: '进行中', color: '#126eed', limit: 2, parentType: 'dev'},
{id: 'story-dev-done', type: 'dev-done', name: '完成', color: '#2fab8e', limit: 2, parentType: 'dev'},
{id: 'story-test', type: 'test', name: '测试', color: '', limit: 4, asParent: true},
{id: 'story-test-doing', type: 'test-doing', name: '进行中', color: '#126eed', limit: 2, parentType: 'test'},
{id: 'story-test-done', type: 'test-done', name: '完成', color: '#2fab8e', limit: 2, parentType: 'test'},
{id: 'story-accepted', type: 'accepted', name: '已验收', color: '', limit: 0},
{id: 'story-published', type: 'published', name: '已发布', color: '', limit: 0},
];
/* Define cards in blacklog column 定义 Blacklog 列上的卡片 */
var blacklogColCards =
[
/* 需求数据结构:
id: ID,通常为需求 ID,
title: 名称,
order: 显示顺序,如果指定为 0,则按 ID 倒序排序,
pri: 优先级,
estimate: 预估时间,
assignedTo: 指派给,
deadline: 截止日期 */
{id: 10010, title: '第一条 Blacklog', order: 1, pri: 1, estimate: 2, assignedTo: 'admin', deadline: '2021-10-30'},
{id: 10011, title: '文档模块的实现', order: 2, pri: 2, estimate: 0, assignedTo: 'sunhao'},
{id: 10012, title: '实现软件需求看板视图中不同分组条件的看板展示方式', order: 3, pri: 3, estimate: 10, assignedTo: 'admin'},
{id: 10013, title: 'Blacklog 3', order: 4, pri: 0, estimate: 0, assignedTo: 'sunhao'},
{id: 10014, title: '实现泳道的更多操作菜单', order: 5, pri: 0, estimate: 0, assignedTo: 'sunhao'},
];
/* Define cards in ready column 定义 准备好 列上的卡片 */
var readyColCards =
[
/* 需求数据结构参见 blacklogColCards 定义 */
{id: 10020, title: '在看板列中实现创建任务功能', order: 10, pri: 1, estimate: 2, assignedTo: 'admin', deadline: '2021-10-30'},
{id: 10021, title: '实现泳道的上下移动', order: 11, pri: 2, estimate: 0, assignedTo: 'sunhao'},
{id: 10024, title: '实现泳道的更多操作菜单', order: 15, pri: 0, estimate: 0, assignedTo: 'sunhao'},
];
var devDoingColCards = /* Define cards in dev/doing column 定义 开发/进行中 列上的卡片 */
[
/* 需求数据结构参见 blacklogColCards 定义 */
{id: 10030, title: '实现看板的更多操作菜单', order: 10, pri: 3, estimate: 0, assignedTo: 'admin', deadline: '2021-10-30'},
{id: 10031, title: '实现卡片拖动效果功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
];
var devDoneColCards = /* Define cards in dev/done column 定义 开发/完成 列上的卡片 */
[
/* 需求数据结构参见 blacklogColCards 定义 */
{id: 10040, title: '实现看板列卡片排序功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
];
var testDoingColCards = /* Define cards in test/doing column 定义 测试/进行中 列上的卡片 */
[
/* 需求数据结构参见 blacklogColCards 定义 */
{id: 10050, title: '实现看板列的更多操作菜单', order: 10, pri: 3, estimate: 0, assignedTo: 'admin', deadline: '2021-10-30'},
{id: 10051, title: '实现Bug卡片的更多操作功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
];
var testDoneColCards = /* Define cards in test/done column 定义 测试/完成 列上的卡片 */
[
/* 需求数据结构参见 blacklogColCards 定义 */
{id: 10060, title: '实现任务卡片的更多操作功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
];
var acceptedColCards =
[
/* 需求数据结构参见 blacklogColCards 定义 */
{id: 10070, title: '实现执行看板的新建按钮组功能', order: 10, pri: 3, estimate: 12, assignedTo: 'admin', deadline: '2021-10-30'},
{id: 10071, title: '在看板列中实现提交Bug功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
{id: 10072, title: '在看板列中实现需求的添加和关联功能', order: 10, pri: 3, estimate: 10, assignedTo: 'admin', deadline: '2021-10-30'},
{id: 10073, title: '实现任务看板视图中不同分组条件的看板展示方式', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
];
var publishedColCards =
[
/* 需求数据结构参见 blacklogColCards 定义 */
{id: 10080, title: '实现任务看板视图中泳道分组下拉菜单功能', order: 10, pri: 3, estimate: 2, assignedTo: 'admin'},
];
/* Define kanban cards in lane 定义看板泳道内每一列的卡片,属性名为看板列类型,属性值为卡片列表 */
var cards =
{
/* 看板列类型: 卡片列表 */
blacklog: blacklogColCards,
ready: readyColCards,
'dev-doing': devDoingColCards,
'dev-done': devDoneColCards,
'test-doing': testDoingColCards,
'test-done': testDoneColCards,
accepted: acceptedColCards,
published: publishedColCards,
};
/* Define kanban lanes 定义看板泳道 */
var lanes =
[
/* 泳道数据结构:
id: 泳道 ID,确保页面上唯一,可以为数字或字符串,
name: 名称,
cards: 定义每列上的卡片
color: 泳道名称背景色
defaultCardType: 看板泳道上的卡片默认类型,例如:'story'(需求) */
{id: 'story', name: '软件需求', cards: cards, color: '#3dc6fc', defaultCardType: 'story'},
]
/* Return kanban data 返回看板数据 */
/* 看板数据结构:
id: ID,确保页面上唯一,可以为数字或字符串,
columns: 定义看板上的所有列,
lanes: 定义看板上的所有泳道
defaultCardType: 看板上的卡片默认类型,例如:'story'(需求) */
return kanbanGroup.story;
return {id: 'story', columns: columns, lanes: lanes, defaultCardType: 'story'};
var link = createLink('execution', 'kanban', "executionID=" + executionID + '&type=' + view);
location.href = link;
}
/**
* Get bug demo kanban data
* 获取“Bug”看板演示数据
* @returns {Object} Kanban data
* @todo 该方法作为演示,应该在最终版本中移除
*/
function getBugKanbanDemoData()
{
return kanbanGroup.bug;
/* Define kanban columns 定义看板列 */
var columns =
[
/* 看板列定义数据结构:
id: 列 ID(确保在页面上唯一),
type: 类型,
name: 显示的名称,
color: 列名称颜色,
parentType: 所属的父级列类型
asParent: 是否作为父级列
cardType: 看板卡片类型,例如:'story'(需求)
limit: 最大数目,该列上允许展示的卡片最大数目,如果为 0 表示无限制 */
{id: 'bug-wait', type: 'wait', name: '待确认', color: '', limit: 0},
{id: 'bug-confirmed', type: 'confirmed', name: '已确认', color: '', limit: 4},
{id: 'bug-resolving', type: 'resolving', name: '解决中', color: '', limit: 4, asParent: true},
{id: 'bug-resolving-doing', type: 'resolving-doing',name: '进行中', color: '#126eed', limit: 2, parentType: 'resolving'},
{id: 'bug-resolving-done', type: 'resolving-done', name: '完成', color: '#2fab8e', limit: 2, parentType: 'resolving'},
{id: 'bug-test', type: 'test', name: '测试', color: '', limit: 4, asParent: true},
{id: 'bug-test-doing', type: 'test-doing', name: '测试中', color: '#126eed', limit: 2, parentType: 'test'},
{id: 'bug-test-done', type: 'test-done', name: '测试完毕', color: '#2fab8e', limit: 2, parentType: 'test'},
{id: 'bug-closed', type: 'closed', name: '已关闭', color: '', limit: 0},
];
/* Define cards in wait column 定义 待确认 列上的卡片 */
var waitColCards =
[
/* bug 数据结构:
id: ID,通常为 Bug ID,
title: 名称,
order: 显示顺序,如果指定为 0,则按 ID 倒序排序,
pri: 优先级,
severity: 紧急程度,
assignedTo: 指派给 */
{id: 10010, title: '在看板列中实现创建任务功能', order: 10, pri: 1, severity: 2, assignedTo: 'admin'},
{id: 10011, title: '实现泳道的上下移动', order: 11, pri: 2, severity: 1, assignedTo: 'sunhao'},
{id: 10014, title: '实现泳道的更多操作菜单', order: 15, pri: 0, severity: 1, assignedTo: 'sunhao'},
];
/* Define cards in confirmed column 定义 已确认 列上的卡片 */
var confirmedColCards =
[
/* Bug 数据结构参见 waitColCards 定义 */
{id: 10020, title: '在看板列中实现创建任务功能', order: 10, pri: 1, severity: 2, assignedTo: 'admin'},
{id: 10021, title: '实现泳道的上下移动', order: 11, pri: 2, severity: 1, assignedTo: 'sunhao'},
{id: 10024, title: '实现泳道的更多操作菜单', order: 15, pri: 0, severity: 1, assignedTo: 'sunhao'},
];
var resolvingDoingColCards = /* Define cards in resolving/doing column 定义 开发/进行中 列上的卡片 */
[
/* Bug 数据结构参见 waitColCards 定义 */
{id: 10030, title: '实现看板的更多操作菜单', order: 10, pri: 3, severity: 1, assignedTo: 'admin'},
{id: 10031, title: '实现卡片拖动效果功能', order: 11, pri: 2, severity: 2, assignedTo: 'sunhao'},
];
var resolvingDoneColCards = /* Define cards in resolving/done column 定义 开发/完成 列上的卡片 */
[
/* Bug 数据结构参见 waitColCards 定义 */
{id: 10040, title: '实现看板列卡片排序功能', order: 10, pri: 1, severity: 4, assignedTo: 'admin'},
];
var testDoingColCards = /* Define cards in test/doing column 定义 测试/进行中 列上的卡片 */
[
/* Bug 数据结构参见 waitColCards 定义 */
{id: 10050, title: '实现看板列的更多操作菜单', order: 10, pri: 3, severity: 3, assignedTo: 'admin'},
{id: 10051, title: '实现Bug卡片的更多操作功能', order: 11, pri: 2, severity: 2, assignedTo: 'sunhao'},
];
var testDoneColCards = /* Define cards in test/done column 定义 测试/完成 列上的卡片 */
[
/* Bug 数据结构参见 waitColCards 定义 */
{id: 10060, title: '实现任务卡片的更多操作功能', order: 10, pri: 1, severity: 4, assignedTo: 'admin'},
];
var closedColCards =
[
/* Bug 数据结构参见 waitColCards 定义 */
{id: 10070, title: '实现执行看板的新建按钮组功能', order: 10, pri: 3, severity: 3, assignedTo: 'admin'},
{id: 10071, title: '在看板列中实现提交Bug功能', order: 11, pri: 2, severity: 2, assignedTo: 'sunhao'},
{id: 10072, title: '在看板列中实现需求的添加和关联功能', order: 10, pri: 3, severity: 3, assignedTo: 'admin'},
{id: 10073, title: '实现任务看板视图中不同分组条件的看板展示方式', order: 11, pri: 2, severity: 2, assignedTo: 'sunhao'},
];
/* Define kanban cards in lane 定义看板泳道内每一列的卡片,属性名为看板列类型,属性值为卡片列表 */
var cards =
{
/* 看板列类型: 卡片列表 */
wait: waitColCards,
confirmed: confirmedColCards,
'resolving-doing': resolvingDoingColCards,
'resolving-done': resolvingDoneColCards,
'test-doing': testDoingColCards,
'test-done': testDoneColCards,
closed: closedColCards,
};
/* Define kanban lanes 定义看板泳道 */
var lanes =
[
/* 泳道数据结构:
id: 泳道 ID,确保页面上唯一,可以为数字或字符串,
name: 名称,
cards: 定义每列上的卡片
color: 泳道名称背景色
defaultCardType: 看板泳道上的卡片默认类型,例如:'bug'(Bug) */
{id: 'bug', name: 'Bug', cards: cards, color: '#9c30b0', defaultCardType: 'bug'},
]
/* Return kanban data 返回看板数据 */
/* 看板数据结构:
id: ID,确保页面上唯一,可以为数字或字符串,
columns: 定义看板上的所有列,
lanes: 定义看板上的所有泳道
defaultCardType: 看板上的卡片默认类型,例如:'bug'(Bug) */
return {id: 'bug', columns: columns, lanes: lanes, defaultCardType: 'bug'};
}
/**
* Get task demo kanban data
* 获取“任务”看板演示数据
* @returns {Object} Kanban data
* @todo 该方法作为演示,应该在最终版本中移除
*/
function getTaskKanbanDemoData()
{
return kanbanGroup.task;
/* Define kanban columns 定义看板列 */
var columns =
[
/* 看板列定义数据结构:
id: 列 ID(确保在页面上唯一),
type: 类型,
name: 显示的名称,
color: 列名称颜色,
parentType: 所属的父级列类型
asParent: 是否作为父级列
cardType: 看板卡片类型,例如:'task'(任务)
limit: 最大数目,该列上允许展示的卡片最大数目,如果为 0 表示无限制 */
{id: 'task-wait', type: 'wait', name: '未开始', color: '', limit: 0},
{id: 'task-dev', type: 'dev', name: '开发', color: '', limit: 4, asParent: true},
{id: 'task-dev-doing', type: 'dev-doing', name: '研发中', color: '#126eed', limit: 2, parentType: 'dev'},
{id: 'task-dev-done', type: 'dev-done', name: '研发完成', color: '#2fab8e', limit: 2, parentType: 'dev'},
{id: 'task-pause', type: 'pause', name: '已暂停', color: '', limit: 0},
{id: 'task-cancel', type: 'cancel', name: '已取消', color: '', limit: 0},
{id: 'task-closed', type: 'closed', name: '已关闭', color: '', limit: 0},
];
/* Define cards in wait column 定义 未开始 列上的卡片 */
var waitColCards =
[
/* 任务数据结构:
id: ID,通常为任务 ID,
name: 名称,
order: 显示顺序,如果指定为 0,则按 ID 倒序排序,
pri: 优先级,
estimate: 预估时间,
assignedTo: 指派给,
deadline: 截止日期 */
{id: 10020, name: '在看板列中实现创建任务功能', order: 10, pri: 1, estimate: 2, assignedTo: 'admin', deadline: '2021-10-30'},
{id: 10021, name: '实现泳道的上下移动', order: 11, pri: 2, estimate: 0, assignedTo: 'sunhao', deadline: '2021-10-22'},
{id: 10024, name: '实现泳道的更多操作菜单', order: 15, pri: 0, estimate: 0, assignedTo: 'sunhao'},
];
var devDoingColCards = /* Define cards in dev/doing column 定义 开发/进行中 列上的卡片 */
[
/* 任务数据结构参见 waitColCards 定义 */
{id: 10030, name: '实现看板的更多操作菜单', order: 10, pri: 3, estimate: 0, assignedTo: 'admin', deadline: '2021-10-30'},
{id: 10031, name: '实现卡片拖动效果功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
];
var devDoneColCards = /* Define cards in dev/done column 定义 开发/完成 列上的卡片 */
[
/* 任务数据结构参见 waitColCards 定义 */
{id: 10040, name: '实现看板列卡片排序功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
];
var pauseColCards = /* Define cards in pause column 定义 已暂停 列上的卡片 */
[
/* 任务数据结构参见 waitColCards 定义 */
{id: 10050, name: '实现看板列的更多操作菜单', order: 10, pri: 3, estimate: 0, assignedTo: 'admin', deadline: '2021-10-30'},
{id: 10051, name: '实现Bug卡片的更多操作功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
];
var cancelColCards = /* Define cards in cancel column 定义 已取消 列上的卡片 */
[
/* 任务数据结构参见 waitColCards 定义 */
{id: 10060, name: '实现任务卡片的更多操作功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
];
var closedColCards =
[
/* 任务数据结构参见 waitColCards 定义 */
{id: 10070, name: '实现执行看板的新建按钮组功能', order: 10, pri: 3, estimate: 12, assignedTo: 'admin', deadline: '2021-10-30'},
{id: 10071, name: '在看板列中实现提交Bug功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
{id: 10072, name: '在看板列中实现任务的添加和关联功能', order: 10, pri: 3, estimate: 10, assignedTo: 'admin', deadline: '2021-10-30'},
{id: 10073, name: '实现任务看板视图中不同分组条件的看板展示方式', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
];
/* Define kanban cards in lane 定义看板泳道内每一列的卡片,属性名为看板列类型,属性值为卡片列表 */
var cards =
{
/* 看板列类型: 卡片列表 */
wait: waitColCards,
'dev-doing': devDoingColCards,
'dev-done': devDoneColCards,
pause: pauseColCards,
cancel: cancelColCards,
closed: closedColCards,
};
/* Define kanban lanes 定义看板泳道 */
var lanes =
[
/* 泳道数据结构:
id: 泳道 ID,确保页面上唯一,可以为数字或字符串,
name: 名称,
cards: 定义每列上的卡片
color: 泳道名称背景色
defaultCardType: 看板泳道上的卡片默认类型,例如:'task'(任务) */
{id: 'task', name: '任务', cards: cards, color: '#126eed', defaultCardType: 'task'},
]
/* Return kanban data 返回看板数据 */
/* 看板数据结构:
id: ID,确保页面上唯一,可以为数字或字符串,
columns: 定义看板上的所有列,
lanes: 定义看板上的所有泳道
defaultCardType: 看板上的卡片默认类型,例如:'task'(任务) */
return {id: 'task', columns: columns, lanes: lanes, defaultCardType: 'task'};
}
/**
* Render user avatar
* @param {String|{account: string, avatar: string}} user User account or user object
* @returns {string}
*/
function renderUserAvatar(user)
function renderUserAvatar(user, objectType, objectID)
{
if(typeof user === 'string') user = {account: user};
if(!user.avatar && window.userList && window.userList[user.account]) user = window.userList[user.account];
return $('<div class="avatar has-text avatar-sm avatar-circle" />').avatar({user: user});
if(objectType == 'task')
{
var link = createLink('task', 'assignto', 'executionID=' + executionID + '&id=' + objectID, '', true);
}
else
{
var link = createLink(objectType, 'assignto', 'id=' + objectID, '', true);
}
return $('<a class="avatar has-text avatar-sm avatar-circle iframe" href="' + link + '"/>').avatar({user: user});
}
/**
* Render deadline
* @param {String|Date} deadline Deadline
* @returns {JQuery}
*/
function renderDeadline(deadline)
{
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();
return $('<span class="info info-deadline"/>').text(deadline).addClass(isEarlyThanToday ? 'text-red' : 'text-muted');
}
/**
@@ -397,14 +71,25 @@ function renderStoryItem(item, $item, col)
'<span class="info info-pri label-pri label-pri-' + item.pri + '" title="' + item.pri + '">' + item.pri + '</span>',
item.estimate ? '<span class="info info-estimate text-muted">' + item.estimate + 'h</span>' : '',
].join(''));
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo));
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo, 'story', item.id));
var $actions = $item.find('.actions');
if(!$actions.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);
}
$item.attr('data-type', 'story').addClass('kanban-item-story');
return $item;
}
/**
* Render bug item 提供方法渲染看板中的 Bug 卡片
* @param {Object} item Bug item object
@@ -434,7 +119,20 @@ function renderBugItem(item, $item, col)
'<span class="info info-severity label-severity" data-severity="' + item.severity + '" title="' + item.severity + '"></span>',
'<span class="info info-pri label-pri label-pri-' + item.pri + '" title="' + item.pri + '">' + item.pri + '</span>',
].join(''));
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo));
if(item.deadline) $infos.append(renderDeadline(item.deadline));
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo, 'bug', item.id));
var $actions = $item.find('.actions');
if(!$actions.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);
}
$item.attr('data-type', 'bug').addClass('kanban-item-bug');
@@ -470,7 +168,20 @@ function renderTaskItem(item, $item, col)
'<span class="info i nfo-pri label-pri label-pri-' + item.pri + '" title="' + item.pri + '">' + item.pri + '</span>',
item.estimate ? '<span class="info info-estimate text-muted">' + item.estimate + 'h</span>' : '',
].join(''));
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo));
if(item.deadline) $infos.append(renderDeadline(item.deadline));
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo, 'task', item.id));
var $actions = $item.find('.actions');
if(!$actions.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');
@@ -478,7 +189,6 @@ function renderTaskItem(item, $item, col)
}
/* Add column renderer/ 添加特定列类型或列条目类型渲染方法 */
/* Add column renderer/ 添加特定列类型或列卡片类型渲染方法 */
addColumnRenderer('story', renderStoryItem);
addColumnRenderer('bug', renderBugItem);
addColumnRenderer('task', renderTaskItem);
@@ -492,10 +202,62 @@ addColumnRenderer('task', renderTaskItem);
*/
function renderColumnCount($count, count, col)
{
var text = count + '/' + (!col.limit ? '<i class="icon icon-infinite"></i>' : '');
var text = count + '/' + (col.limit < 0 ? '<i class="icon icon-infinite"></i>' : col.limit);
$count.html(text + '<i class="icon icon-arrow-up"></i>');
}
/**
* Render header column 渲染看板列头部
* @param {JQuery} $col Header column element
* @param {Object} col Header column object
* @param {JQuery} $header Header element
* @param {Object} kanban Kanban object
*/
function renderHeaderCol($col, col, $header, kanban)
{
if(col.asParent) $col = $col.children('.kanban-header-col');
if(!$col.children('.actions').length)
{
var $actions = $('<div class="actions" />');
if(col.type === 'backlog' || col.type === 'wait' || col.type == 'unconfirmed')
{
$actions.append([
'<a data-contextmenu="columnCreate" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '" class="text-primary">',
'<i class="icon icon-expand-alt"></i>',
'</a>'
].join(''));
}
$actions.append([
'<a data-contextmenu="column" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>'
].join(''));
$actions.appendTo($col);
}
}
/**
* Render lane name 渲染看板泳道名称
* @param {JQuery} $name Name element
* @param {Object} lane Lane object
* @param {JQuery} $kanban $kanban element
* @param {Object} columns Kanban columns
* @param {Object} kanban Kanban object
*/
function renderLaneName($name, lane, $kanban, columns, kanban)
{
if(!$name.children('.actions').length)
{
$([
'<div class="actions">',
'<a data-contextmenu="lane" data-lane="' + lane.id + '" data-kanban="' + kanban.id + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>',
'</div>'
].join('')).appendTo($name);
}
}
/**
* Updata kanban data
* 更新看板上的数据
@@ -657,6 +419,148 @@ function handleFinishDrop(event)
$('#kanbans').find('.can-drop-here').removeClass('can-drop-here');
}
/**
* Handle sort cards in column 处理对列卡片进行排序
*/
function handleSortColCards()
{
/* TODO: handle sort cards from column contextmenu */
}
/**
* Create column menu 创建列操作菜单
* @returns {Object[]}
*/
function createColumnMenu(options)
{
var $col = options.$trigger.closest('.kanban-col');
var col = $col.data('col');
var kanbanID = options.kanban;
var items = [];
if(priv.hasEditName) items.push({label: executionLang.editName, url: $.createLink('kanban', 'setColumn', 'col=' + col.columnID + '&executionID=' + executionID), className: 'iframe'})
if(priv.hasSetWIP) items.push({label: executionLang.setWIP, url: $.createLink('kanban', 'setWIP', 'col=' + col.columnID + '&executionID=' + executionID), className: 'iframe'})
items.push({label: executionLang.sortColumn, items: ['按ID倒序', '按ID顺序'], className: 'iframe', onClick: handleSortColCards})
return items;
}
/**
* Create column create button menu 创建列添加按钮操作菜单
* @returns {Object[]}
*/
function createColumnCreateMenu(options)
{
var $col = options.$trigger.closest('.kanban-col');
var col = $col.data('col');
var items = [];
if(col.laneType == 'story')
{
if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID)});
if(priv.canBatchCreateStory) items.push({label: storyLang.batchCreate, url: $.createLink('story', 'batchcreate', 'productID=' + productID)});
if(priv.canLinkStory) items.push({label: executionLang.linkStory, url: $.createLink('execution', 'linkStory', 'executionID=' + executionID)});
if(priv.canLinkStoryByPlane) items.push({label: executionLang.linkStoryByPlan, url: '#linkStoryByPlan', 'attrs' : {'data-toggle': 'modal'}});
}
else if(col.laneType == 'bug')
{
if(priv.canCreateBug) items.push({label: bugLang.create, url: $.createLink('bug', 'create', 'productID=0&moduleID=0&extra=executionID=' + executionID)});
if(priv.canBatchCreateBug) items.push({label: bugLang.batchCreate, url: $.createLink('bug', 'batchcreate', 'productID=' + productID + '&moduleID=0&executionID=' + executionID)});
}
else
{
if(priv.canCreateTask) items.push({label: taskLang.create, url: $.createLink('task', 'create', 'executionID=' + executionID)});
if(priv.canBatchCreateTask) items.push({label: taskLang.batchCreate, url: $.createLink('task', 'batchcreate', 'executionID=' + executionID)});
}
return items;
}
/**
* Create lane menu 创建泳道操作菜单
* @returns {Object[]}
*/
function createLaneMenu(options)
{
var $lane = options.$trigger.closest('.kanban-lane');
var $kanban = $lane.closest('.kanban');
var lane = $lane.data('lane');
var kanbanID = options.kanban;
var items =
[
{label: '泳道设置', icon: 'edit', url: $.createLink('kanban', 'laneedit', 'lane=' + lane.id + '&kanban=' + kanbanID), className: 'iframe'},
{label: '泳道上移', icon: 'arrow-up', url: $.createLink('kanban', 'lanemove', 'direction=up&lane=' + lane.id + '&kanban=' + kanbanID), className: 'iframe', disabled: !$kanban.prev('.kanban').length},
{label: '泳道下移', icon: 'arrow-down', url: $.createLink('kanban', 'lanemove', 'direction=down&lane=' + lane.id + '&kanban=' + kanbanID), className: 'iframe', disabled: !$kanban.next('.kanban').length},
];
var bounds = options.$trigger[0].getBoundingClientRect();
items.$options = {x: bounds.right, y: bounds.top};
return items;
}
/**
* Create story menu 创建需求卡片操作菜单
* @returns {Object[]}
*/
function createStoryMenu(options)
{
var $card = options.$trigger.closest('.kanban-item');
var story = $card.data('item');
var items =
[
{label: '编辑需求', icon: 'edit', url: $.createLink('story', 'edit', 'storyID=' + story.id), className: 'iframe'},
{label: '变更需求', icon: 'change', url: $.createLink('story', 'change', 'storyID=' + story.id), className: 'iframe'},
{label: '移除需求', icon: 'unlink', url: $.createLink('story', 'unlink', 'storyID=' + story.id), className: 'iframe'},
{label: '分解任务', icon: 'plus', url: $.createLink('task', 'create', 'storyID=' + story.id), className: 'iframe'},
{label: '批量分解', icon: 'pluses', url: $.createLink('task', 'batchCreate', 'storyID=' + story.id), className: 'iframe'},
];
return items;
}
/**
* Create bug menu 创建 Bug 卡片操作菜单
* @returns {Object[]}
*/
function createBugMenu(options)
{
var $card = options.$trigger.closest('.kanban-item');
var bug = $card.data('item');
var items =
[
{label: '编辑Bug', icon: 'edit', url: $.createLink('bug', 'edit', 'bugID=' + bug.id), className: 'iframe'},
{label: '确认Bug', icon: 'ok', url: $.createLink('bug', 'confirm', 'bugID=' + bug.id), className: 'iframe'},
{label: '复制Bug', icon: 'copy', url: $.createLink('bug', 'copy', 'bugID=' + bug.id), className: 'iframe'},
{label: '转软件需求', icon: 'lightbulb', url: $.createLink('story', 'create', 'bugID=' + bug.id), className: 'iframe'},
];
return items;
}
/**
* Create task menu 创建任务卡片操作菜单
* @returns {Object[]}
*/
function createTaskMenu(options)
{
var $card = options.$trigger.closest('.kanban-item');
var task = $card.data('item');
var items =
[
{label: '编辑任务', icon: 'edit', url: $.createLink('task', 'edit', 'taskID=' + task.id), className: 'iframe'},
{label: '拆分子任务', icon: 'plus', url: $.createLink('task', 'create', 'taskID=' + task.id), className: 'iframe'},
{label: '复制任务', icon: 'copy', url: $.createLink('task', 'copy', 'taskID=' + task.id), className: 'iframe'},
{label: '取消任务', icon: 'cancel', url: $.createLink('task', 'cancel', 'taskID=' + task.id), className: 'iframe'},
];
return items;
}
/* Define menu creators */
window.menuCreators =
{
column: createColumnMenu,
columnCreate: createColumnCreateMenu,
lane: createLaneMenu,
story: createStoryMenu,
bug: createBugMenu,
task: createTaskMenu,
};
/* Overload kanban default options */
$.extend($.fn.kanban.Constructor.DEFAULTS,
{
@@ -683,26 +587,66 @@ $(function()
showCount: true,
showZeroCount: true,
fluidBoardWidth: true,
countRender: renderColumnCount,
droppable:
{
target: findDropColumns,
finish: handleFinishDrop,
mouseButton: 'left'
}
},
onRenderHeaderCol: renderHeaderCol,
onRenderLaneName: renderLaneName,
onRenderCount: renderColumnCount
};
/* Hide group menu. */
hideGroupMenu();
/* Create story kanban 创建需求看板 */
createKanban('story', getStoryKanbanDemoData(), commonOptions);
if(browseType == 'all' || browseType == 'story') createKanban('story', kanbanGroup.story, commonOptions);
/* Create bug kanban 创建 Bug 看板 */
createKanban('bug', getBugKanbanDemoData(), commonOptions);
if(browseType == 'all' || browseType == 'bug') createKanban('bug', kanbanGroup.bug, commonOptions);
/* Create task kanban 创建 任务 看板 */
createKanban('task', getTaskKanbanDemoData(), commonOptions);
if(browseType == 'all' || browseType == 'task') createKanban('task', kanbanGroup.task, commonOptions);
/* Init iframe modals */
$(document).on('click', '.iframe', function(event)
{
$(this).modalTrigger({show: true, width: '500px'});
event.preventDefault();
});
/* Init contextmenu */
$('#kanbans').on('click', '[data-contextmenu]', function(event)
{
var $trigger = $(this);
var menuType = $trigger.data('contextmenu');
var menuCreator = window.menuCreators[menuType];
if(!menuCreator) return;
var options = $.extend({event, $trigger: $trigger}, $trigger.data());
var items = menuCreator(options);
if(!items || !items.length) return;
$.zui.ContextMenu.show(items, items.$options || {event: event});
});
/* Hide contextmenu when page scroll */
$(window).on('scroll', function()
{
$.zui.ContextMenu.hide();
});
$('#toStoryButton').on('click', function()
{
var planID = $('#plan').val();
if(planID)
{
parent.location.href = createLink('execution', 'importPlanStories', 'executionID=' + executionID + '&planID=' + planID);
}
})
});
/**
+5
View File
@@ -376,6 +376,11 @@ $lang->execution->kanbanHideCols = 'Geschlossene und abgebrochene Spalten in K
$lang->execution->kanbanShowOption = 'Aufklappen';
$lang->execution->kanbanColsColor = 'Spaltenfarben';
$lang->execution->kanbanViewList['all'] = 'All';
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}";
$lang->execution->kanbanViewList['bug'] = 'Bug';
$lang->execution->kanbanViewList['task'] = 'Task';
$lang->kanbanSetting = new stdclass();
$lang->kanbanSetting->noticeReset = 'Möchten Sie die Einstellungen des Kanbans zurücksetzen?';
$lang->kanbanSetting->optionList['0'] = 'Verstecken';
+8
View File
@@ -101,6 +101,9 @@ $lang->execution->copyNoExecution = 'There are no ' . $lang->executionCommon . '
$lang->execution->noTeam = 'No team members at the moment';
$lang->execution->or = ' or ';
$lang->execution->selectProject = 'Please select project';
$lang->execution->editName = 'Edit Name';
$lang->execution->setWIP = 'WIP Settings';
$lang->execution->sortColumn = 'Kanban Card Sorting';
/* Fields of zt_team. */
$lang->execution->root = 'Root';
@@ -385,6 +388,11 @@ $lang->execution->kanbanHideCols = 'Closed & Cancelled Columns';
$lang->execution->kanbanShowOption = 'Unfold';
$lang->execution->kanbanColsColor = 'Customize Column Color';
$lang->execution->kanbanViewList['all'] = 'All';
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}";
$lang->execution->kanbanViewList['bug'] = 'Bug';
$lang->execution->kanbanViewList['task'] = 'Task';
$lang->kanbanSetting = new stdclass();
$lang->kanbanSetting->noticeReset = 'Do you want to reset Kanban?';
$lang->kanbanSetting->optionList['0'] = 'Hide';
+5
View File
@@ -376,6 +376,11 @@ $lang->execution->kanbanHideCols = 'Colonnes masquées';
$lang->execution->kanbanShowOption = 'Déplier';
$lang->execution->kanbanColsColor = 'Personnalisation Couleurs';
$lang->execution->kanbanViewList['all'] = 'All';
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}";
$lang->execution->kanbanViewList['bug'] = 'Bug';
$lang->execution->kanbanViewList['task'] = 'Task';
$lang->kanbanSetting = new stdclass();
$lang->kanbanSetting->noticeReset = 'Voulez-vous réinitialiser le tableau Kanban ?';
$lang->kanbanSetting->optionList['0'] = 'Masquer';
+5
View File
@@ -377,6 +377,11 @@ $lang->execution->kanbanHideCols = 'Cột Đã đóng & đã hủy';
$lang->execution->kanbanShowOption = 'Mở ra';
$lang->execution->kanbanColsColor = 'Tùy biến màu cột';
$lang->execution->kanbanViewList['all'] = 'All';
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}";
$lang->execution->kanbanViewList['bug'] = 'Bug';
$lang->execution->kanbanViewList['task'] = 'Task';
$lang->kanbanSetting = new stdclass();
$lang->kanbanSetting->noticeReset = 'Bạn có muốn thiết lập lại Kanban?';
$lang->kanbanSetting->optionList['0'] = 'Ẩn';
+8
View File
@@ -101,6 +101,9 @@ $lang->execution->copyNoExecution = '没有可用的' . $lang->executionCommon .
$lang->execution->noTeam = '暂时没有团队成员';
$lang->execution->or = '或';
$lang->execution->selectProject = '请选择项目';
$lang->execution->editName = '编辑名称';
$lang->execution->setWIP = '在制品数量设置(WIP)';
$lang->execution->sortColumn = '看板列卡片排序';
/* Fields of zt_team. */
$lang->execution->root = '源ID';
@@ -386,6 +389,11 @@ $lang->execution->kanbanHideCols = '看板隐藏已关闭、已取消列';
$lang->execution->kanbanShowOption = '显示折叠信息';
$lang->execution->kanbanColsColor = '看板列自定义颜色';
$lang->execution->kanbanViewList['all'] = '综合看板';
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}看板";
$lang->execution->kanbanViewList['bug'] = 'Bug看板';
$lang->execution->kanbanViewList['task'] = '任务看板';
$lang->kanbanSetting = new stdclass();
$lang->kanbanSetting->noticeReset = '是否恢复看板默认设置?';
$lang->kanbanSetting->optionList['0'] = '隐藏';
+28 -1
View File
@@ -81,6 +81,8 @@
</ul>
</div>
<?php endif;?>
<?php else:?>
<?php $canCreateTask = $canBatchCreateTask = $canCreateBug = $canBatchCreateBug = $canCreateStory = $canBatchCreateStory = $canLinkStory = $canLinkStoryByPlane = false;?>
<?php endif;?>
</div>
</div>
@@ -104,12 +106,37 @@
<div class="modal-body">
<div class='input-group'>
<?php echo html::select('plan', $allPlans, '', "class='form-control chosen' id='plan'");?>
<span class='input-group-btn'><?php echo html::commonButton($lang->execution->linkStory, "id='toTaskButton'", 'btn btn-primary');?></span>
<span class='input-group-btn'><?php echo html::commonButton($lang->execution->linkStory, "id='toStoryButton'", 'btn btn-primary');?></span>
</div>
</div>
</div>
</div>
</div>
<?php js::set('executionID', $executionID);?>
<?php js::set('productID', $productID);?>
<?php js::set('kanbanGroup', $kanbanGroup);?>
<?php js::set('browseType', $browseType);?>
<?php
js::set('priv',
array(
'hasEditName' => common::hasPriv('kanban', 'setColumn'),
'hasSetWIP' => common::hasPriv('kanban', 'setWIP'),
'canCreateTask' => $canCreateTask,
'canBatchCreateTask' => $canBatchCreateTask,
'canCreateBug' => $canCreateBug,
'canBatchCreateBug' => $canBatchCreateBug,
'canCreateStory' => $canCreateStory,
'canBatchCreateStory' => $canBatchCreateStory,
'canLinkStory' => $canLinkStory,
'canLinkStoryByPlane' => $canLinkStoryByPlane,
)
);
?>
<?php js::set('executionLang', $lang->execution);?>
<?php js::set('storyLang', $lang->story);?>
<?php js::set('taskLang', $lang->task);?>
<?php js::set('bugLang', $lang->bug);?>
<?php js::set('editName', $lang->execution->editName);?>
<?php js::set('setWIP', $lang->execution->setWIP);?>
<?php js::set('sortColumn', $lang->execution->sortColumn);?>
<?php include '../../common/view/footer.html.php';?>