@@ -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 ) ;
}
} )
} ) ;
/**