Merge branch 'sprint/168_sgm_kanban' into 'sprint/168'
* Add kanban view. See merge request easycorp/zentaopms!154
This commit is contained in:
@@ -1849,20 +1849,13 @@ class execution extends control
|
||||
|
||||
$this->execution->setMenu($executionID);
|
||||
$execution = $this->loadModel('execution')->getById($executionID);
|
||||
$tasks = $this->execution->getKanbanTasks($executionID, "id");
|
||||
$bugs = $this->loadModel('bug')->getExecutionBugs($executionID);
|
||||
$stories = $this->loadModel('story')->getExecutionStories($executionID);
|
||||
|
||||
/* Determines whether an object is editable. */
|
||||
$canBeChanged = common::canModify('execution', $execution);
|
||||
|
||||
$kanbanGroup = $this->execution->getKanbanGroupData($stories, $tasks, $bugs, $type);
|
||||
$kanbanSetting = $this->execution->getKanbanSetting();
|
||||
|
||||
$this->view->title = $this->lang->execution->kanban;
|
||||
$this->view->position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
|
||||
$this->view->position[] = $this->lang->execution->kanban;
|
||||
$this->view->stories = $stories;
|
||||
$this->view->realnames = $this->loadModel('user')->getPairs('noletter');
|
||||
$this->view->storyOrder = $orderBy;
|
||||
$this->view->orderBy = 'id_asc';
|
||||
@@ -1870,11 +1863,6 @@ class execution extends control
|
||||
$this->view->browseType = '';
|
||||
$this->view->execution = $execution;
|
||||
$this->view->type = $type;
|
||||
$this->view->kanbanGroup = $kanbanGroup;
|
||||
$this->view->kanbanColumns = $this->execution->getKanbanColumns($kanbanSetting);
|
||||
$this->view->statusMap = $canBeChanged ? $this->execution->getKanbanStatusMap($kanbanSetting) : array();
|
||||
$this->view->statusList = $this->execution->getKanbanStatusList($kanbanSetting);
|
||||
$this->view->colorList = $this->execution->getKanbanColorList($kanbanSetting);
|
||||
$this->view->canBeChanged = $canBeChanged;
|
||||
|
||||
$this->display();
|
||||
|
||||
+541
-245
@@ -1,253 +1,549 @@
|
||||
/**
|
||||
* Get story demo kanban data
|
||||
* 获取“软件需求”看板演示数据
|
||||
* @returns {Object} Kanban data
|
||||
* @todo 该方法作为演示,应该在最终版本中移除
|
||||
*/
|
||||
function getStoryKanbanDemoData()
|
||||
{
|
||||
/* Define kanban columns 定义看板列 */
|
||||
var columns =
|
||||
[
|
||||
/* 看板列定义数据结构:
|
||||
id: 列 ID(确保在页面上唯一),
|
||||
type: 类型,
|
||||
name: 显示的名称,
|
||||
color: 列名称颜色,
|
||||
parentType: 所属的父级列类型
|
||||
asParent: 是否作为父级列
|
||||
itemType: 看板条目类型,例如:'story'(需求)
|
||||
maxCount: 最大数目,该列上允许展示的条目最大数目,如果为 0 表示无限制 */
|
||||
{id: 'story-blacklog', type: 'blacklog', name: 'Blacklog', color: '', maxCount: 0},
|
||||
{id: 'story-ready', type: 'ready', name: '准备好', color: '', maxCount: 4},
|
||||
{id: 'story-dev', type: 'dev', name: '开发', color: '', maxCount: 4, asParent: true},
|
||||
{id: 'story-dev-doing', type: 'dev-doing', name: '进行中', color: '#126eed', maxCount: 2, parentType: 'dev'},
|
||||
{id: 'story-dev-done', type: 'dev-done', name: '完成', color: '#2fab8e', maxCount: 2, parentType: 'dev'},
|
||||
{id: 'story-test', type: 'test', name: '测试', color: '', maxCount: 4, asParent: true},
|
||||
{id: 'story-test-doing', type: 'test-doing', name: '进行中', color: '#126eed', maxCount: 2, parentType: 'test'},
|
||||
{id: 'story-test-done', type: 'test-done', name: '完成', color: '#2fab8e', maxCount: 2, parentType: 'test'},
|
||||
{id: 'story-accepted', type: 'accepted', name: '已验收', color: '', maxCount: 0},
|
||||
{id: 'story-published', type: 'published', name: '已发布', color: '', maxCount: 0},
|
||||
];
|
||||
|
||||
/* Define items in blacklog column 定义 Blacklog 列上的条目 */
|
||||
var blacklogColItems =
|
||||
[
|
||||
/* 需求数据结构:
|
||||
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 items in ready column 定义 准备好 列上的条目 */
|
||||
var readyColItems =
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColItems 定义 */
|
||||
{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 devDoingColItems = /* Define items in dev/doing column 定义 开发/进行中 列上的条目 */
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColItems 定义 */
|
||||
{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 devDoneColItems = /* Define items in dev/done column 定义 开发/完成 列上的条目 */
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColItems 定义 */
|
||||
{id: 10040, title: '实现看板列卡片排序功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
];
|
||||
var testDoingColItems = /* Define items in test/doing column 定义 测试/进行中 列上的条目 */
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColItems 定义 */
|
||||
{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 testDoneColItems = /* Define items in test/done column 定义 测试/完成 列上的条目 */
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColItems 定义 */
|
||||
{id: 10060, title: '实现任务卡片的更多操作功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
];
|
||||
var acceptedColItems =
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColItems 定义 */
|
||||
{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 publishedColItems =
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColItems 定义 */
|
||||
{id: 10080, title: '实现任务看板视图中泳道分组下拉菜单功能', order: 10, pri: 3, estimate: 2, assignedTo: 'admin'},
|
||||
];
|
||||
|
||||
/* Define kanban items in lane 定义看板泳道内每一列的条目,属性名为看板列类型,属性值为条目列表 */
|
||||
var items =
|
||||
{
|
||||
/* 看板列类型: 条目列表 */
|
||||
blacklog: blacklogColItems,
|
||||
ready: readyColItems,
|
||||
'dev-doing': devDoingColItems,
|
||||
'dev-done': devDoneColItems,
|
||||
'test-doing': testDoingColItems,
|
||||
'test-done': testDoneColItems,
|
||||
accepted: acceptedColItems,
|
||||
published: publishedColItems,
|
||||
};
|
||||
|
||||
/* Define kanban lanes 定义看板泳道 */
|
||||
var lanes =
|
||||
[
|
||||
/* 泳道数据结构:
|
||||
id: 泳道 ID,确保页面上唯一,可以为数字或字符串,
|
||||
name: 名称,
|
||||
items: 定义每列上的条目
|
||||
color: 泳道名称背景色
|
||||
defaultItemType: 看板泳道上的条目默认类型,例如:'story'(需求) */
|
||||
{id: 'story', name: '软件需求', items: items, color: '#3dc6fc', defaultItemType: 'story'},
|
||||
]
|
||||
|
||||
/* Return kanban data 返回看板数据 */
|
||||
/* 看板数据结构:
|
||||
id: ID,确保页面上唯一,可以为数字或字符串,
|
||||
columns: 定义看板上的所有列,
|
||||
lanes: 定义看板上的所有泳道
|
||||
defaultItemType: 看板上的条目默认类型,例如:'story'(需求) */
|
||||
return {id: 'story', columns: columns, lanes: lanes, defaultItemType: 'story'};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bug demo kanban data
|
||||
* 获取“Bug”看板演示数据
|
||||
* @returns {Object} Kanban data
|
||||
* @todo 该方法作为演示,应该在最终版本中移除
|
||||
*/
|
||||
function getBugKanbanDemoData()
|
||||
{
|
||||
/* Define kanban columns 定义看板列 */
|
||||
var columns =
|
||||
[
|
||||
/* 看板列定义数据结构:
|
||||
id: 列 ID(确保在页面上唯一),
|
||||
type: 类型,
|
||||
name: 显示的名称,
|
||||
color: 列名称颜色,
|
||||
parentType: 所属的父级列类型
|
||||
asParent: 是否作为父级列
|
||||
itemType: 看板条目类型,例如:'story'(需求)
|
||||
maxCount: 最大数目,该列上允许展示的条目最大数目,如果为 0 表示无限制 */
|
||||
{id: 'bug-wait', type: 'wait', name: '待确认', color: '', maxCount: 0},
|
||||
{id: 'bug-confirmed', type: 'confirmed', name: '已确认', color: '', maxCount: 4},
|
||||
{id: 'bug-resolving', type: 'resolving', name: '解决中', color: '', maxCount: 4, asParent: true},
|
||||
{id: 'bug-resolving-doing', type: 'resolving-doing',name: '进行中', color: '#126eed', maxCount: 2, parentType: 'resolving'},
|
||||
{id: 'bug-resolving-done', type: 'resolving-done', name: '完成', color: '#2fab8e', maxCount: 2, parentType: 'resolving'},
|
||||
{id: 'bug-test', type: 'test', name: '测试', color: '', maxCount: 4, asParent: true},
|
||||
{id: 'bug-test-doing', type: 'test-doing', name: '测试中', color: '#126eed', maxCount: 2, parentType: 'test'},
|
||||
{id: 'bug-test-done', type: 'test-done', name: '测试完毕', color: '#2fab8e', maxCount: 2, parentType: 'test'},
|
||||
{id: 'bug-closed', type: 'closed', name: '已关闭', color: '', maxCount: 0},
|
||||
];
|
||||
|
||||
/* Define items in wait column 定义 待确认 列上的条目 */
|
||||
var waitColItems =
|
||||
[
|
||||
/* 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 items in confirmed column 定义 已确认 列上的条目 */
|
||||
var confirmedColItems =
|
||||
[
|
||||
/* Bug 数据结构参见 waitColItems 定义 */
|
||||
{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 resolvingDoingColItems = /* Define items in resolving/doing column 定义 开发/进行中 列上的条目 */
|
||||
[
|
||||
/* Bug 数据结构参见 waitColItems 定义 */
|
||||
{id: 10030, title: '实现看板的更多操作菜单', order: 10, pri: 3, severity: 1, assignedTo: 'admin'},
|
||||
{id: 10031, title: '实现卡片拖动效果功能', order: 11, pri: 2, severity: 2, assignedTo: 'sunhao'},
|
||||
];
|
||||
var resolvingDoneColItems = /* Define items in resolving/done column 定义 开发/完成 列上的条目 */
|
||||
[
|
||||
/* Bug 数据结构参见 waitColItems 定义 */
|
||||
{id: 10040, title: '实现看板列卡片排序功能', order: 10, pri: 1, severity: 4, assignedTo: 'admin'},
|
||||
];
|
||||
var testDoingColItems = /* Define items in test/doing column 定义 测试/进行中 列上的条目 */
|
||||
[
|
||||
/* Bug 数据结构参见 waitColItems 定义 */
|
||||
{id: 10050, title: '实现看板列的更多操作菜单', order: 10, pri: 3, severity: 3, assignedTo: 'admin'},
|
||||
{id: 10051, title: '实现Bug卡片的更多操作功能', order: 11, pri: 2, severity: 2, assignedTo: 'sunhao'},
|
||||
];
|
||||
var testDoneColItems = /* Define items in test/done column 定义 测试/完成 列上的条目 */
|
||||
[
|
||||
/* Bug 数据结构参见 waitColItems 定义 */
|
||||
{id: 10060, title: '实现任务卡片的更多操作功能', order: 10, pri: 1, severity: 4, assignedTo: 'admin'},
|
||||
];
|
||||
var closedColItems =
|
||||
[
|
||||
/* Bug 数据结构参见 waitColItems 定义 */
|
||||
{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 items in lane 定义看板泳道内每一列的条目,属性名为看板列类型,属性值为条目列表 */
|
||||
var items =
|
||||
{
|
||||
/* 看板列类型: 条目列表 */
|
||||
wait: waitColItems,
|
||||
confirmed: confirmedColItems,
|
||||
'resolving-doing': resolvingDoingColItems,
|
||||
'resolving-done': resolvingDoneColItems,
|
||||
'test-doing': testDoingColItems,
|
||||
'test-done': testDoneColItems,
|
||||
closed: closedColItems,
|
||||
};
|
||||
|
||||
/* Define kanban lanes 定义看板泳道 */
|
||||
var lanes =
|
||||
[
|
||||
/* 泳道数据结构:
|
||||
id: 泳道 ID,确保页面上唯一,可以为数字或字符串,
|
||||
name: 名称,
|
||||
items: 定义每列上的条目
|
||||
color: 泳道名称背景色
|
||||
defaultItemType: 看板泳道上的条目默认类型,例如:'bug'(Bug) */
|
||||
{id: 'bug', name: 'Bug', items: items, color: '#9c30b0', defaultItemType: 'bug'},
|
||||
]
|
||||
|
||||
/* Return kanban data 返回看板数据 */
|
||||
/* 看板数据结构:
|
||||
id: ID,确保页面上唯一,可以为数字或字符串,
|
||||
columns: 定义看板上的所有列,
|
||||
lanes: 定义看板上的所有泳道
|
||||
defaultItemType: 看板上的条目默认类型,例如:'bug'(Bug) */
|
||||
return {id: 'bug', columns: columns, lanes: lanes, defaultItemType: 'bug'};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task demo kanban data
|
||||
* 获取“任务”看板演示数据
|
||||
* @returns {Object} Kanban data
|
||||
* @todo 该方法作为演示,应该在最终版本中移除
|
||||
*/
|
||||
function getTaskKanbanDemoData()
|
||||
{
|
||||
/* Define kanban columns 定义看板列 */
|
||||
var columns =
|
||||
[
|
||||
/* 看板列定义数据结构:
|
||||
id: 列 ID(确保在页面上唯一),
|
||||
type: 类型,
|
||||
name: 显示的名称,
|
||||
color: 列名称颜色,
|
||||
parentType: 所属的父级列类型
|
||||
asParent: 是否作为父级列
|
||||
itemType: 看板条目类型,例如:'task'(任务)
|
||||
maxCount: 最大数目,该列上允许展示的条目最大数目,如果为 0 表示无限制 */
|
||||
{id: 'task-wait', type: 'wait', name: '未开始', color: '', maxCount: 0},
|
||||
{id: 'task-dev', type: 'dev', name: '开发', color: '', maxCount: 4, asParent: true},
|
||||
{id: 'task-dev-doing', type: 'dev-doing', name: '研发中', color: '#126eed', maxCount: 2, parentType: 'dev'},
|
||||
{id: 'task-dev-done', type: 'dev-done', name: '研发完成', color: '#2fab8e', maxCount: 2, parentType: 'dev'},
|
||||
{id: 'task-pause', type: 'pause', name: '已暂停', color: '', maxCount: 0},
|
||||
{id: 'task-cancel', type: 'cancel', name: '已取消', color: '', maxCount: 0},
|
||||
{id: 'task-closed', type: 'closed', name: '已关闭', color: '', maxCount: 0},
|
||||
];
|
||||
|
||||
/* Define items in wait column 定义 未开始 列上的条目 */
|
||||
var waitColItems =
|
||||
[
|
||||
/* 任务数据结构:
|
||||
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 devDoingColItems = /* Define items in dev/doing column 定义 开发/进行中 列上的条目 */
|
||||
[
|
||||
/* 任务数据结构参见 waitColItems 定义 */
|
||||
{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 devDoneColItems = /* Define items in dev/done column 定义 开发/完成 列上的条目 */
|
||||
[
|
||||
/* 任务数据结构参见 waitColItems 定义 */
|
||||
{id: 10040, name: '实现看板列卡片排序功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
];
|
||||
var pauseColItems = /* Define items in pause column 定义 已暂停 列上的条目 */
|
||||
[
|
||||
/* 任务数据结构参见 waitColItems 定义 */
|
||||
{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 cancelColItems = /* Define items in cancel column 定义 已取消 列上的条目 */
|
||||
[
|
||||
/* 任务数据结构参见 waitColItems 定义 */
|
||||
{id: 10060, name: '实现任务卡片的更多操作功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
];
|
||||
var closedColItems =
|
||||
[
|
||||
/* 任务数据结构参见 waitColItems 定义 */
|
||||
{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 items in lane 定义看板泳道内每一列的条目,属性名为看板列类型,属性值为条目列表 */
|
||||
var items =
|
||||
{
|
||||
/* 看板列类型: 条目列表 */
|
||||
wait: waitColItems,
|
||||
'dev-doing': devDoingColItems,
|
||||
'dev-done': devDoneColItems,
|
||||
pause: pauseColItems,
|
||||
cancel: cancelColItems,
|
||||
closed: closedColItems,
|
||||
};
|
||||
|
||||
/* Define kanban lanes 定义看板泳道 */
|
||||
var lanes =
|
||||
[
|
||||
/* 泳道数据结构:
|
||||
id: 泳道 ID,确保页面上唯一,可以为数字或字符串,
|
||||
name: 名称,
|
||||
items: 定义每列上的条目
|
||||
color: 泳道名称背景色
|
||||
defaultItemType: 看板泳道上的条目默认类型,例如:'task'(任务) */
|
||||
{id: 'task', name: '任务', items: items, color: '#126eed', defaultItemType: 'task'},
|
||||
]
|
||||
|
||||
/* Return kanban data 返回看板数据 */
|
||||
/* 看板数据结构:
|
||||
id: ID,确保页面上唯一,可以为数字或字符串,
|
||||
columns: 定义看板上的所有列,
|
||||
lanes: 定义看板上的所有泳道
|
||||
defaultItemType: 看板上的条目默认类型,例如:'task'(任务) */
|
||||
return {id: 'task', columns: columns, lanes: lanes, defaultItemType: 'task'};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render user account
|
||||
* @param {String} userAccount User account
|
||||
* @returns {string}
|
||||
*/
|
||||
function renderUserAvatar(userAccount)
|
||||
{
|
||||
var hue = $.zui.strCode(userAccount) * 43 / 360;
|
||||
return ('<div class="avatar has-text avatar-sm avatar-circle" style="background: hsl(' + hue + ', 40%, 60%);"><span>'
|
||||
+ userAccount[0].toUpperCase()
|
||||
+ '</span></div>');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $title = $item.find('.title');
|
||||
if(!$title.length)
|
||||
{
|
||||
$title = $('<a class="title iframe"><i class="icon icon-lightbulb text-muted"></i> <span class="text"></span></a>')
|
||||
.attr('href', $.createLink('story', 'view', 'storyID=' + item.id));
|
||||
$title.appendTo($item);
|
||||
}
|
||||
$title.attr('title', item.title).find('.text').text(item.title);
|
||||
|
||||
var $infos = $item.find('.infos');
|
||||
if(!$infos.length)
|
||||
{
|
||||
$infos = $('<div class="infos"></div>').appendTo($item);
|
||||
}
|
||||
$infos.html(
|
||||
[
|
||||
'<span class="info info-id text-muted">#' + item.id + '</span>',
|
||||
'<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>' : '',
|
||||
item.assignedTo ? renderUserAvatar(item.assignedTo) : '',
|
||||
].join(''));
|
||||
|
||||
$item.attr('data-type', 'story').addClass('kanban-item-story');
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render bug item 提供方法渲染看板中的 Bug 条目
|
||||
* @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 $title = $item.find('.title');
|
||||
if(!$title.length)
|
||||
{
|
||||
$title = $('<a class="title iframe"><i class="icon icon-bug text-muted"></i> <span class="text"></span></a>')
|
||||
.attr('href', $.createLink('bug', 'view', 'bugID=' + item.id));
|
||||
$title.appendTo($item);
|
||||
}
|
||||
$title.attr('title', item.title).find('.text').text(item.title);
|
||||
|
||||
var $infos = $item.find('.infos');
|
||||
if(!$infos.length)
|
||||
{
|
||||
$infos = $('<div class="infos"></div>').appendTo($item);
|
||||
}
|
||||
$infos.html(
|
||||
[
|
||||
'<span class="info info-id text-muted">#' + item.id + '</span>',
|
||||
'<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>',
|
||||
item.assignedTo ? renderUserAvatar(item.assignedTo) : '',
|
||||
].join(''));
|
||||
|
||||
$item.attr('data-type', 'bug').addClass('kanban-item-bug');
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $title = $item.find('.title');
|
||||
if(!$title.length)
|
||||
{
|
||||
$title = $('<a class="title iframe"><i class="icon icon-lightbulb text-muted"></i> <span class="text"></span></a>')
|
||||
.attr('href', $.createLink('task', 'view', 'taskID=' + item.id));
|
||||
$title.appendTo($item);
|
||||
}
|
||||
$title.attr('title', item.name).find('.text').text(item.name);
|
||||
|
||||
var $infos = $item.find('.infos');
|
||||
if(!$infos.length)
|
||||
{
|
||||
$infos = $('<div class="infos"></div>').appendTo($item);
|
||||
}
|
||||
$infos.html(
|
||||
[
|
||||
'<span class="info info-id text-muted">#' + item.id + '</span>',
|
||||
'<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>' : '',
|
||||
item.assignedTo ? renderUserAvatar(item.assignedTo) : '',
|
||||
].join(''));
|
||||
|
||||
$item.attr('data-type', 'task').addClass('kanban-item-task');
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
|
||||
/* Add column renderer/ 添加特定列类型或列条目类型渲染方法 */
|
||||
addColumnRenderer('story', renderStoryItem);
|
||||
addColumnRenderer('bug', renderBugItem);
|
||||
addColumnRenderer('task', renderTaskItem);
|
||||
|
||||
/**
|
||||
* Render column count 渲染看板列头上的条目数目
|
||||
* @param {JQuery} $count Kanban count element
|
||||
* @param {number} count Column items count
|
||||
* @param {number} col Column object
|
||||
* @param {Object} kanban Kanban intance
|
||||
*/
|
||||
function renderColumnCount($count, count, col)
|
||||
{
|
||||
var text = count + '/' + (!col.maxCount ? '<i class="icon icon-infinite"></i>' : '');
|
||||
$count.html(text + '<i class="icon icon-arrow-up"></i>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updata kanban data
|
||||
* 更新看板上的数据
|
||||
* @param {string} kanbanID Kanban id 看板 ID
|
||||
* @param {Object} data Kanban data 看板数据
|
||||
*/
|
||||
function updateKanban(kanbanID, data)
|
||||
{
|
||||
var $kanban = $('#kanban-' + kanbanID);
|
||||
if(!$kanban.length) return;
|
||||
|
||||
$kanban.data('zui.kanban').render(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create kanban in page
|
||||
* 在界面上创建一个看板界面
|
||||
* @param {string} kanbanID Kanban id 看板 ID
|
||||
* @param {Object} data Kanban data 看板数据
|
||||
* @param {Object} options Kanban options 组件初始化数据 看板名称
|
||||
*/
|
||||
function createKanban(kanbanID, data, options)
|
||||
{
|
||||
var $kanban = $('#kanban-' + kanbanID);
|
||||
if($kanban.length) return updateKanban(kanbanID, data);
|
||||
|
||||
$kanban = $('<div id="kanban-' + kanbanID + '"></div>').appendTo('#kanbans');
|
||||
$kanban.kanban($.extend({data: data}, options));
|
||||
}
|
||||
|
||||
/* Example code: */
|
||||
$(function()
|
||||
{
|
||||
var isFirefox = $.zui.browser.firefox;
|
||||
var adjustBoardsHeight = function()
|
||||
/* Common options 用于初始化看板的通用选项 */
|
||||
var commonOptions =
|
||||
{
|
||||
var $cBoards = $('.c-boards');
|
||||
var viewHeight = $(window).height() - $('#header').height() - $('#footer').height() - 111;
|
||||
if ($cBoards.length === 1)
|
||||
{
|
||||
var $boardsWrapper = $cBoards.find('.boards-wrapper');
|
||||
$boardsWrapper.css('min-height', viewHeight);
|
||||
if($boardsWrapper.height() > $boardsWrapper.find('.boards').height())
|
||||
{
|
||||
$boardsWrapper.find('.boards').css(isFirefox ? 'height' : 'min-height', $boardsWrapper.height() - 1);
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
$cBoards.each(function()
|
||||
{
|
||||
var $theBoards = $(this);
|
||||
|
||||
var $boardsWrapper = $theBoards.find('.boards-wrapper');
|
||||
var minHeight = Math.min($theBoards.prev().find('.board-story').outerHeight() + 4, viewHeight);
|
||||
$boardsWrapper.css({maxHeight: viewHeight, minHeight: minHeight});
|
||||
if($boardsWrapper.height() > $boardsWrapper.find('.boards').height())
|
||||
{
|
||||
var $boards = $boardsWrapper.find('.boards');
|
||||
$boards.css({maxHeight: $theBoards.height(), minHeight: minHeight});
|
||||
if ($boards.outerHeight() < minHeight) $boards.css('height', minHeight);
|
||||
}
|
||||
});
|
||||
};
|
||||
adjustBoardsHeight();
|
||||
|
||||
var boardID = '';
|
||||
var onlybody = config.requestType == 'GET' ? "&onlybody=yes" : "?onlybody=yes";
|
||||
$.cookie('selfClose', 0, {expires:config.cookieLife, path:config.webRoot});
|
||||
var $kanban = $('#kanban');
|
||||
|
||||
// Get scrollbar width
|
||||
var getScrollbarWidth = function ()
|
||||
{
|
||||
var outer = document.createElement("div");
|
||||
outer.style.visibility = "hidden";
|
||||
outer.style.width = "100px";
|
||||
outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps
|
||||
|
||||
document.body.appendChild(outer);
|
||||
|
||||
var widthNoScroll = outer.offsetWidth;
|
||||
// force scrollbars
|
||||
outer.style.overflow = "scroll";
|
||||
|
||||
// add innerdiv
|
||||
var inner = document.createElement("div");
|
||||
inner.style.width = "100%";
|
||||
outer.appendChild(inner);
|
||||
|
||||
var widthWithScroll = inner.offsetWidth;
|
||||
|
||||
// remove divs
|
||||
outer.parentNode.removeChild(outer);
|
||||
|
||||
return widthNoScroll - widthWithScroll;
|
||||
maxColHeight: 'auto',
|
||||
minColWidth: 240,
|
||||
dropable: true,
|
||||
showCount: true,
|
||||
showZeroCount: true,
|
||||
countRender: renderColumnCount
|
||||
};
|
||||
|
||||
var scrollbarWidth = getScrollbarWidth();
|
||||
var fixBoardWidth = function()
|
||||
{
|
||||
var $table = $kanban.children('.table:first');
|
||||
var kanbanWidth = $table.width();
|
||||
var $cBoards = $table.find('thead>tr>th.c-board:not(.c-side)');
|
||||
var boardCount = $cBoards.length;
|
||||
var $cSide = $table.find('thead>tr>th.c-board.c-side');
|
||||
var totalWidth = kanbanWidth - scrollbarWidth - 1;
|
||||
if ($cSide.length) totalWidth = totalWidth - ($cSide.outerWidth() + 5);
|
||||
var cBoardWidth = Math.floor(totalWidth/boardCount);
|
||||
$cBoards.not(':last').width(cBoardWidth);
|
||||
if ($cSide.length) $cBoards.first().width(cBoardWidth + (isFirefox ? 0 : 5));
|
||||
$kanban.find('.boards > .board').width(cBoardWidth - (isFirefox ? 21 : 22));
|
||||
};
|
||||
fixBoardWidth();
|
||||
/* Create story kanban 创建需求看板 */
|
||||
createKanban('story', getStoryKanbanDemoData(), commonOptions);
|
||||
|
||||
var updateUI = function()
|
||||
{
|
||||
fixBoardWidth();
|
||||
adjustBoardsHeight();
|
||||
$kanban.data('zui.table').updateFixUI();
|
||||
};
|
||||
/* Create bug kanban 创建 Bug 看板 */
|
||||
createKanban('bug', getBugKanbanDemoData(), commonOptions);
|
||||
|
||||
$(window).on('resize', updateUI);
|
||||
|
||||
var refresh = function(force)
|
||||
{
|
||||
var selfClose = $.cookie('selfClose');
|
||||
$.cookie('selfClose', 0, {expires:config.cookieLife, path:config.webRoot});
|
||||
if(selfClose == 1 || force)
|
||||
{
|
||||
$kanban.load(location.href + ' #kanban>*', updateUI);
|
||||
}
|
||||
};
|
||||
window.refreshKanban = refresh;
|
||||
|
||||
var kanbanModalTrigger = new $.zui.ModalTrigger({type: 'iframe', width: 800});
|
||||
var dropTo = function(id, from, to, type)
|
||||
{
|
||||
if(statusMap[type][from] && statusMap[type][from][to])
|
||||
{
|
||||
var method = statusMap[type][from][to];
|
||||
var link = $.createLink(type, method, 'id=' + id + '&subStatus=' + to);
|
||||
if(method == 'ajaxChangeSubStatus')
|
||||
{
|
||||
$.getJSON(link, function(response)
|
||||
{
|
||||
if(response.result == 'fail' && response.message)
|
||||
{
|
||||
bootAlert(response.message);
|
||||
setTimeout(function(){location.reload();}, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
kanbanModalTrigger.show(
|
||||
{
|
||||
url: link + onlybody,
|
||||
shown: function(){$('.modal-iframe').addClass('with-titlebar').data('cancel-reload', true)},
|
||||
width: 900,
|
||||
hidden: refresh
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep the draged element stay in the new place. */
|
||||
return true;
|
||||
};
|
||||
|
||||
$kanban.droppable(
|
||||
{
|
||||
selector: '.board-item:not(.disabled)',
|
||||
target: function($ele)
|
||||
{
|
||||
var itemType = $ele.data('type');
|
||||
var $board = $ele.closest('.board');
|
||||
var type = $board.data('type');
|
||||
return $board.siblings('.board').filter(function()
|
||||
{
|
||||
var typeMap = statusMap[itemType];
|
||||
var actionMap = typeMap && typeMap[type];
|
||||
return !!actionMap && actionMap[$(this).data('type')];
|
||||
});
|
||||
},
|
||||
start: function(e)
|
||||
{
|
||||
$kanban.addClass('dragging');
|
||||
e.targets.addClass('can-drop-in');
|
||||
var $item = $(e.element).addClass('dragging');
|
||||
$item.closest('.boards').addClass('dragging');
|
||||
},
|
||||
drag: function(e)
|
||||
{
|
||||
var $item = $(e.element);
|
||||
var $target = $(e.target);
|
||||
var $holder = $target.find('.board-drag-holder');
|
||||
if (!$holder.length) $holder = $('<div class="board-drag-holder"></div>').appendTo($target);
|
||||
$kanban.find('.c-board.dragging').removeClass('dragging');
|
||||
$kanban.find('.c-board.s-' + $target.data('type')).addClass('dragging');
|
||||
$holder.height($item.outerHeight());
|
||||
},
|
||||
drop: function(e)
|
||||
{
|
||||
var result = dropTo(e.element.data('id'), e.element.closest('.board').data('type'), e.target.data('type'), e.element.data('type'));
|
||||
if(result !== false)
|
||||
{
|
||||
e.element.insertBefore(e.target.find('.board-drag-holder'));
|
||||
}
|
||||
},
|
||||
finish: function()
|
||||
{
|
||||
$kanban.removeClass('dragging').find('.can-drop-in').removeClass('can-drop-in');
|
||||
$kanban.find('.dragging').removeClass('dragging');
|
||||
}
|
||||
});
|
||||
|
||||
$kanban.on('click', '.kanbaniframe', function(e)
|
||||
{
|
||||
var $link = $(this);
|
||||
kanbanModalTrigger.show(
|
||||
{
|
||||
url: $link.attr('href'),
|
||||
shown: function(){$('.modal-iframe').addClass('with-titlebar').data('cancel-reload', true)},
|
||||
hidden: refresh,
|
||||
width: $(this).is('.task-assignedTo,.bug-assignedTo') ? 800 : 1100
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
fixKanbanSide($kanban);
|
||||
/* Create task kanban 创建 任务 看板 */
|
||||
createKanban('task', getTaskKanbanDemoData(), commonOptions);
|
||||
});
|
||||
|
||||
function fixKanbanSide($kanban)
|
||||
{
|
||||
if($kanban.length == 0) return false;
|
||||
|
||||
fixSideInit();
|
||||
$kanban.scroll(fixSide);//Fix kanban side when scrolling.
|
||||
|
||||
var tableWidth, kanbanOffset, fixedSide, $fixedSide;
|
||||
function fixSide()
|
||||
{
|
||||
kanbanOffset = $kanban.offset().left;
|
||||
$fixedSide = $kanban.parent().find('.fixedSide');
|
||||
if($fixedSide.length <= 0 && kanbanOffset < $kanban.scrollLeft())
|
||||
{
|
||||
var $th = $kanban.find('table thead tr th:first');
|
||||
|
||||
tableWidth = $th.width();
|
||||
|
||||
fixedSide = "<table class='table table-bordered fixedSide'><thead><tr><th class='c-board c-side has-btn'>" + $th.html()+ '</th></tr></thead><tbody>';
|
||||
$kanban.find('table tbody tr').each(function()
|
||||
{
|
||||
var $td = $(this).find('td:first');
|
||||
fixedSide = fixedSide + "<tr><td class='c-side text-left'>" + $td.html() + '</td></tr>';
|
||||
});
|
||||
fixedSide = fixedSide + '</tbody></table>';
|
||||
|
||||
$kanban.before(fixedSide);
|
||||
|
||||
$('.fixedSide').width(tableWidth);
|
||||
$('.fixedSide').css('top', $kanban.offset());
|
||||
|
||||
/* Reset height. */
|
||||
var index = 1;
|
||||
$('.fixedSide tbody tr').each(function()
|
||||
{
|
||||
var $td = $kanban.find('table tbody tr:nth-child(' + index + ') td:first');
|
||||
|
||||
if($(this).find('td:first div:first').length == 0) $(this).find('td:first').html('<div></div>');
|
||||
$(this).find('td:first div:first').height($td.height());
|
||||
|
||||
index++;
|
||||
})
|
||||
}
|
||||
if($fixedSide.length > 0 && kanbanOffset >= $kanban.scrollLeft()) $fixedSide.remove();
|
||||
}
|
||||
function fixSideInit()
|
||||
{
|
||||
$fixedSide = $kanban.parent().find('.fixedSide');
|
||||
if($fixedSide.length > 0) $fixedSide.remove();
|
||||
fixSide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
*/
|
||||
?>
|
||||
<?php include '../../common/view/header.html.php';?>
|
||||
<?php js::set('statusMap', $statusMap);?>
|
||||
<?php include '../../common/view/kanban.html.php';?>
|
||||
|
||||
<div id='mainMenu' class='clearfix'>
|
||||
<div class='btn-toolbar pull-right'>
|
||||
<?php echo html::a($this->createLink('execution', 'ajaxKanbanSetting', "executionID=$executionID"), "<i class='icon-cog muted'></i> " . $lang->execution->kanbanSetting, '', "class='iframe btn btn-link'");?>
|
||||
@@ -46,189 +47,15 @@
|
||||
<?php endif;?>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
<?php foreach($colorList as $status => $color):?>
|
||||
<?php echo "#kanban .c-board.s-$status{border-color: " . ($color ? $color : '#000') . ";}\n"?>
|
||||
<?php endforeach?>
|
||||
</style>
|
||||
<div id="kanban" class="main-table fade auto-fade-in" data-ride="table" data-checkable="false" data-group="true">
|
||||
<?php
|
||||
$hasTask = false;
|
||||
foreach($kanbanGroup as $group)
|
||||
{
|
||||
if(count(get_object_vars($group)) > 0)
|
||||
{
|
||||
$hasTask = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php if(!$hasTask):?>
|
||||
<div class="table-empty-tip">
|
||||
<p>
|
||||
<span class="text-muted"><?php echo $lang->task->noTask;?></span>
|
||||
<?php if($canBeChanged and common::hasPriv('task', 'create', $checkObject)):?>
|
||||
<?php echo html::a($this->createLink('task', 'create', "execution=$executionID" . (isset($moduleID) ? "&storyID=&moduleID=$moduleID" : '')), "<i class='icon icon-plus'></i> " . $lang->task->create, '', "class='btn btn-info'");?>
|
||||
<?php endif;?>
|
||||
</p>
|
||||
|
||||
<div class='panel'>
|
||||
<div class='panel-heading'>
|
||||
<strong>Section</strong>
|
||||
</div>
|
||||
<div class='panel-body'>
|
||||
<div id='kanbans'></div>
|
||||
</div>
|
||||
<?php else:?>
|
||||
<table class="table no-margin table-grouped text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="c-board c-side has-btn">
|
||||
<div class="dropdown">
|
||||
<?php $dropTitle = $type == 'story' ? $lang->execution->orderList[$storyOrder] : $lang->task->$type;?>
|
||||
<button type="button" data-toggle="dropdown" class="btn btn-block btn-link"><?php echo $dropTitle;?> <span class="caret"></span></button>
|
||||
<ul class='dropdown-menu text-left'>
|
||||
<?php foreach($lang->execution->orderList as $key => $value):?>
|
||||
<li <?php echo ($type == 'story' and $storyOrder == $key) ? " class='active'" : '' ?>>
|
||||
<?php echo html::a($this->createLink('execution', 'kanban', "executionID=$executionID&type=story&orderBy=$key"), $value);?>
|
||||
</li>
|
||||
<?php endforeach;?>
|
||||
<?php echo "<li" . ($type == 'assignedTo' ? " class='active'" : '') . ">" . html::a(inlink('kanban', "execution=$executionID&type=assignedTo"), $lang->execution->groups['assignedTo']) . "</li>";?>
|
||||
<?php echo "<li" . ($type == 'finishedBy' ? " class='active'" : '') . ">" . html::a(inlink('kanban', "execution=$executionID&type=finishedBy"), $lang->execution->groups['finishedBy']) . "</li>";?>
|
||||
</ul>
|
||||
</div>
|
||||
</th>
|
||||
<?php foreach($kanbanColumns as $col):?>
|
||||
<th class='c-board s-<?php echo $col?>'><?php echo zget($statusList, $col);?></th>
|
||||
<?php endforeach;?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php $rowIndex = 0; ?>
|
||||
<?php foreach($kanbanGroup as $groupKey => $group):?>
|
||||
<?php if(count(get_object_vars($group)) == 0) continue;?>
|
||||
<tr data-id='<?php echo $rowIndex++?>'>
|
||||
<td class='c-side text-left'>
|
||||
<?php if($groupKey != 'nokey'):?>
|
||||
<?php if($type == 'story'):?>
|
||||
<?php $story = $group;?>
|
||||
<div class='board-story' data-id='<?php echo $story->id;?>'>
|
||||
<div class='board-title'>
|
||||
<?php
|
||||
if(common::hasPriv('story', 'view'))
|
||||
{
|
||||
echo html::a($this->createLink('story', 'view', "storyID=$story->id", '', true), $story->title, '', 'class="kanbaniframe group-title" title="' . $story->title . '"');
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<span class='group-title' title='{$story->title}'>{$story->title}</span>";
|
||||
}
|
||||
?>
|
||||
<?php if($canBeChanged):?>
|
||||
<nav class='board-actions nav nav-default'>
|
||||
<li class='dropdown'>
|
||||
<a href='javascript:;' data-toggle='dropdown' class='panel-action'><i class='icon icon-ellipsis-v'></i></a>
|
||||
<ul class='dropdown-menu pull-right'>
|
||||
<?php
|
||||
$misc = "data-toggle='modal' data-type='iframe' data-width='95%'";
|
||||
echo (common::hasPriv('task', 'create')) ? '<li>' . html::a($this->createLink('task', 'create', "executionID=$executionID&storyID=$story->id&moduleID=$story->module", '', true), $lang->execution->wbs, '', $misc) : '' . '</li>';
|
||||
echo (common::hasPriv('task', 'batchCreate')) ? '<li>' . html::a($this->createLink('task', 'batchCreate', "executionID=$executionID&storyID=$story->id&moduleID=0&taskID=0&iframe=true", '', true), $lang->execution->batchWBS, '', $misc) : '' . '</li>';
|
||||
echo (common::hasPriv('execution', 'unlinkStory')) ? '<li>' . html::a($this->createLink('execution', 'unlinkStory', "executionID=$executionID&storyID=$story->story&confirm=no", '', true), $lang->execution->unlinkStory, 'hiddenwin') : '' . '</li>';
|
||||
$misc = "data-toggle='modal' data-type='iframe'";
|
||||
echo (common::hasPriv('story', 'close')) ? '<li>' . html::a($this->createLink('story', 'close', "storyID=$story->id", '', true), $lang->story->close, '', $misc) : '' . '</li>';
|
||||
?>
|
||||
</ul>
|
||||
</li>
|
||||
</nav>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
<div class="small group-info">
|
||||
<span class='story-id board-id' title='<?php echo $lang->story->id?>'>#<?php echo $story->id?></span>
|
||||
<span class='label-pri label-pri-<?php echo $story->pri?>' title='<?php echo $lang->story->pri?>'><?php echo zget($lang->story->priList, $story->pri);?></span>
|
||||
<span class='story-stage' title='<?php echo $lang->story->stage?>'><span class="label label-dot"></span> <?php echo $lang->story->stageList[$story->stage];?></span>
|
||||
<div class='pull-right text-muted story-estimate' title='<?php echo $lang->story->estimate?>'><?php echo $story->estimate . 'h ';?></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php else:?>
|
||||
<div class='board-story' data-id='<?php echo $groupKey?>'><?php echo zget($realnames, $groupKey);?></div>
|
||||
<?php endif;?>
|
||||
<?php endif;?>
|
||||
</td>
|
||||
<td class="c-boards no-padding text-left" colspan="<?php echo count($kanbanColumns);?>">
|
||||
<div class="boards-wrapper">
|
||||
<div class="boards">
|
||||
<?php foreach($kanbanColumns as $col):?>
|
||||
<div class="board" data-type="<?php echo $col;?>">
|
||||
<?php if(!empty($group->tasks[$col])):?>
|
||||
<?php foreach($group->tasks[$col] as $task):?>
|
||||
<?php $disabled = common::hasDBPriv($task, 'task') ? '' : 'disabled';?>
|
||||
<div class='board-item <?php echo $disabled;?>' data-id='<?php echo $task->id?>' id='task-<?php echo $task->id?>' data-type='task'>
|
||||
<?php
|
||||
$childrenAB = $task->parent > 0 ? "<span class='label label-light label-badge'>" . $lang->task->childrenAB . '</span> ' : '';
|
||||
if(common::hasPriv('task', 'view'))
|
||||
{
|
||||
echo html::a($this->createLink('task', 'view', "taskID=$task->id", '', true), "{$childrenAB}{$task->name}", '', 'class="title kanbaniframe" title="' . $task->name . '"');
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<span class='title' title='{$task->name}'>{$childrenAB}{$task->name}</span>";
|
||||
}
|
||||
?>
|
||||
<div class='info'>
|
||||
<?php
|
||||
$assignedToRealName = "<span>" . zget($realnames, $task->assignedTo) . "</span>";
|
||||
if(empty($task->assignedTo)) $assignedToRealName = "<span class='text-primary'>{$lang->task->noAssigned}</span>";
|
||||
if(common::hasPriv('task', 'assignTo', $task))
|
||||
{
|
||||
echo html::a($this->createLink('task', 'assignTo', "executionID={$task->execution}&taskID={$task->id}", '', true), '<i class="icon icon-hand-right"></i> ' . $assignedToRealName, '', 'class="btn btn-icon-left kanbaniframe task-assignedTo"');
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<span class='btn btn-icon-left task-assignedTo disabled'><i class='icon icon-hand-right'></i> {$assignedToRealName}</span>";
|
||||
}
|
||||
?>
|
||||
<?php if(isset($task->delay)):?>
|
||||
<span class="status-task status-delayed"> <?php echo $lang->task->delayed;?></span>
|
||||
<?php endif;?>
|
||||
<small class="task-left" title='<?php echo $lang->task->left?>'><?php echo $task->left;?>h</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach?>
|
||||
<?php endif?>
|
||||
<?php if(!empty($group->bugs[$col])):?>
|
||||
<?php foreach($group->bugs[$col] as $bug):?>
|
||||
<div class='board-item' data-id='<?php echo $bug->id?>' id='bug-<?php echo $bug->id?>' data-type='bug'>
|
||||
<?php
|
||||
if(common::hasPriv('bug', 'view'))
|
||||
{
|
||||
echo html::a($this->createLink('bug', 'view', "bugID=$bug->id", '', true), "<i class='icon icon-sm icon-bug text-red'></i> #{$bug->id}{$bug->title}", '', 'class="title kanbaniframe" title="' . $bug->title . '"');
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<span class='title' title='{$bug->title}'><i class='icon icon-sm icon-bug text-red'></i> #{$bug->id}{$bug->title}</span>";
|
||||
}
|
||||
?>
|
||||
<div class='info'>
|
||||
<?php
|
||||
$assignedToRealName = "<span>" . zget($realnames, $bug->assignedTo) . "</span>";
|
||||
if(empty($bug->assignedTo)) $assignedToRealName = "<span class='text-primary'>{$lang->task->noAssigned}</span>";
|
||||
if(common::hasPriv('bug', 'assignTo', $bug))
|
||||
{
|
||||
echo html::a($this->createLink('bug', 'assignTo', "bugID={$bug->id}", '', true), '<i class="icon icon-hand-right"></i> ' . $assignedToRealName, '', 'class="btn btn-icon-left kanbaniframe bug-assignedTo"');
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<span class='btn btn-icon-left bug-assignedTo disabled'><i class='icon icon-hand-right'></i> {$assignedToRealName}</span>";
|
||||
}
|
||||
?>
|
||||
<span class='status-bug status-<?php echo $bug->status;?>' title='<?php echo $lang->bug->status?>'><span class="label label-dot"></span> <?php echo zget($lang->bug->statusList, $bug->status);?></span>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach?>
|
||||
<?php endif?>
|
||||
</div>
|
||||
<?php endforeach;?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach;?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
<?php echo js::set('executionID', $executionID);?>
|
||||
<?php js::set('executionID', $executionID);?>
|
||||
<?php //js::set('statusMap', $statusMap);?>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
|
||||
@@ -59,8 +59,14 @@ class kanbanModel extends model
|
||||
}
|
||||
}
|
||||
|
||||
foreach($lanes as $laneID => $lane) $lane->columns = $columns[$laneID];
|
||||
return $lanes;
|
||||
$kanban = array();
|
||||
foreach($lanes as $laneID => $lane)
|
||||
{
|
||||
$lane->columns = $columns[$laneID];
|
||||
$kanban[$lane->type] = $lane;
|
||||
}
|
||||
|
||||
return $kanban;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user