* task #44357, refactor executions kanban with kanban component and support dnd.
This commit is contained in:
@@ -43,9 +43,13 @@
|
||||
#kanbanList .kanban-header-col[data-type="doingProject"] + .kanban-header-col[data-type="doingExecution"] {padding: 38px 10px 0;}
|
||||
#kanbanList .kanban-header-col[data-type="doingProject"]:after {content: attr(data-span-text); display: block; position: absolute; z-index: 10; left: 0; right: -100%; right: calc(-100% - 2px); top: 0; line-height: 36px; text-align: center; font-weight: bold; border-bottom: 2px solid #fff; background-color: #ededed;}
|
||||
#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-lane-items {height: 100%; display: flex; flex-direction: column; justify-content: center;}
|
||||
#kanbanList .kanban-item.kanban-item-span,
|
||||
#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-item {background-color: transparent; border: none; padding: 0; text-align: center; box-shadow: none!important;}
|
||||
#kanbanList .kanban-item.kanban-item-span:hover,
|
||||
#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-item:hover {box-shadow: none;}
|
||||
#kanbanList .kanban-item.kanban-item-span > .title,
|
||||
#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-item > .title {white-space: normal;}
|
||||
|
||||
#kanbanList .kanban-col[data-type="normalRelease"] .kanban-item > .title {display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center; height: 38px;}
|
||||
#kanbanList .kanban-col[data-type="normalRelease"] .kanban-item > .title > .text {display: block; white-space: nowrap; text-overflow: clip; overflow: hidden;}
|
||||
#kanbanList .kanban-col[data-type="normalRelease"] .kanban-item > .title.has-icon > .text {margin-right: 5px; max-width: calc(100% - 20px);}
|
||||
@@ -84,6 +88,25 @@ function isEarlierThanToday(date)
|
||||
return $.zui.createDate(date).getTime() < window.todayBegin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render normal text span item
|
||||
* @param {Object} item Product item object
|
||||
* @param {JQuery} $item Kanban item element
|
||||
* @param {Object} col Column object
|
||||
* @returns {JQuery} $item Kanban item element
|
||||
*/
|
||||
function renderSpanItem(item, $item)
|
||||
{
|
||||
console.log('renderSpanItem', item, $item);
|
||||
var $title = $item.find('.title');
|
||||
if(!$title.length)
|
||||
{
|
||||
$title = $('<div class="title" />').appendTo($item);
|
||||
}
|
||||
$title.text(item.name).attr('title', item.name);
|
||||
return $item.addClass('kanban-item-span');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render product item
|
||||
* @param {Object} item Product item object
|
||||
@@ -301,6 +324,8 @@ function renderDoingProjectItem(item, $item)
|
||||
/** All build-in columns renderers */
|
||||
if(!window.columnRenderers) window.columnRenderers =
|
||||
{
|
||||
span: renderSpanItem,
|
||||
execution: renderExecutionItem,
|
||||
unclosedProduct: renderProductItem,
|
||||
unexpiredPlan: renderPlanItem,
|
||||
waitProject: renderProjectItem,
|
||||
@@ -334,8 +359,7 @@ function addColumnRenderer(columnType, renderer)
|
||||
function renderKanbanItem(item, $item, col, lane, kanban)
|
||||
{
|
||||
var columnRenderers = window.columnRenderers;
|
||||
var cardType = col.cardType || lane.defaultCardType || kanban.defaultCardType;
|
||||
var renderer = columnRenderers[col.type] || columnRenderers[cardType];
|
||||
var renderer = columnRenderers[col.cardType] || columnRenderers[col.type] || columnRenderers[lane.defaultCardType || kanban.defaultCardType];
|
||||
if(renderer) return renderer(item, $item, col);
|
||||
return $item;
|
||||
}
|
||||
@@ -404,7 +428,7 @@ $.extend($.fn.kanban.Constructor.DEFAULTS,
|
||||
showZeroCount: true,
|
||||
onRenderHeaderCol: function($col, col)
|
||||
{
|
||||
if(col.type === 'doingProject') $col.attr('data-span-text', doingText);
|
||||
if(col.type === 'doingProject' && window.doingText) $col.attr('data-span-text', window.doingText);
|
||||
},
|
||||
onRenderKanban: function($kanban, kanbanData)
|
||||
{
|
||||
|
||||
@@ -1,24 +1,7 @@
|
||||
.main-table .table {cursor: default; border-collapse: unset;}
|
||||
|
||||
.boards {display: table; table-layout: fixed; width: 100%; min-width: 140px;}
|
||||
.board {display: table-cell; width: 25%; padding: 10px;}
|
||||
.board > div {max-height: 230px; overflow: auto;}
|
||||
.board:not(:last-child), .board-project {border-right: 2px solid #fff;}
|
||||
.board-item {border: 1px solid #EBEBEB; padding: 5px 10px; cursor: default; border-radius: 2px; background-color: #fff;}
|
||||
.board-item:hover {border-color: #ccc;}
|
||||
.board-item + .board-item {margin-top: 10px;}
|
||||
|
||||
#kanban {overflow-y: auto;}
|
||||
#kanban thead > tr > th:not(:last-child) {border-right: 2px solid #efefef;}
|
||||
#kanban tbody > tr > td {line-height: 24px;}
|
||||
#kanban tbody > tr > td {background-color: #F5F5F5;}
|
||||
#kanban tbody > tr:not(:last-child) > td {border-bottom: 5px solid #fff !important;}
|
||||
|
||||
.s-doing .table-row .table-col:last-child, .c-progress {width: 30px; padding-left: 3px; padding-top: 1px;}
|
||||
.table-row .table-col:first-child, .board-project {overflow: hidden; white-space: nowrap;}
|
||||
|
||||
.table-grouped > tbody > tr,
|
||||
.table-grouped > tbody > tr:hover {background: #fff !important;}
|
||||
|
||||
.projectColor {width: 5px; padding: 0px !important; border-right: unset !important}
|
||||
th .count {font-weight: 500; color: #8b91a2;}
|
||||
#kanbanList .panel-body {padding: 10px!important;}
|
||||
#kanbanList .kanban-header {min-height: 32px!important;}
|
||||
#kanbanList .kanban-header-col {height: 32px!important; padding: 0!important;}
|
||||
#kanbanList .kanban-header-col > .title {padding: 0 5px!important;}
|
||||
#kanbanList .kanban-col[data-type="project"] .kanban-lane-items {height: 100%; display: flex; flex-direction: column; justify-content: center;}
|
||||
#kanbanList .kanban-lane-name {width: 5px; margin-right: 15px;}
|
||||
#kanbanList .kanban-lane-name > .text {display: none;}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Process kanban data
|
||||
* @returns {Object} kanban data
|
||||
*/
|
||||
function processKanbanData()
|
||||
{
|
||||
/* Generate columns */
|
||||
var columns = [{id: 'project', type: 'project', name: window.langDoingProject, cardType: 'span'}];
|
||||
$.each(kanbanColumns, function(type, name)
|
||||
{
|
||||
columns.push({id: type, type: type, name: name, cardType: 'execution'});
|
||||
});
|
||||
|
||||
/* Format lanes data */
|
||||
var lanes = [];
|
||||
$.each(kanbanGroup, function(projectID, statusMap)
|
||||
{
|
||||
var projectName = +projectID ? projectNames[projectID] : langMyExecutions;
|
||||
var cards = {project: [{id: projectID, name: projectName}]};
|
||||
|
||||
$.each(kanbanColumns, function(type)
|
||||
{
|
||||
var cardList = [];
|
||||
var executions = statusMap[type];
|
||||
|
||||
if(!executions) return;
|
||||
|
||||
$.each(executions, function(index, execution)
|
||||
{
|
||||
var executionCard = $.extend({}, execution, {id: projectID + '-' + execution.id, _id: execution.id});
|
||||
cardList.push(executionCard);
|
||||
});
|
||||
cards[type] = cardList;
|
||||
});
|
||||
|
||||
lanes.push({id: projectID, name: projectName, cards: cards});
|
||||
});
|
||||
|
||||
return {id: 'executions', columns: columns, lanes: lanes};
|
||||
}
|
||||
|
||||
/*
|
||||
* Find drop columns
|
||||
* @param {JQuery} $element Drag element
|
||||
* @param {JQuery} $root Dnd root element
|
||||
*/
|
||||
function findDropColumns($element, $root)
|
||||
{
|
||||
var $col = $element.closest('.kanban-col');
|
||||
var col = $col.data();
|
||||
var lane = $col.closest('.kanban-lane').data();
|
||||
var kanbanID = $root.data('id');
|
||||
console.log('findDropColumns', {$element, $root, kanbanID, col, lane});
|
||||
var kanbanRules = window.kanbanDropRules ? window.kanbanDropRules[kanbanID] : null;
|
||||
|
||||
if(!kanbanRules) return $root.find('.kanban-lane[data-id="' + lane.id + '"] .kanban-lane-col:not([data-type="project"],[data-type="' + col.type + '"])');
|
||||
|
||||
var colRules = kanbanRules[col.type];
|
||||
var lane = $col.closest('.kanban-lane').data('lane');
|
||||
return $root.find('.kanban-lane-col').filter(function()
|
||||
{
|
||||
if(!colRules) return false;
|
||||
if(colRules === true) return true;
|
||||
|
||||
var $newCol = $(this);
|
||||
var newCol = $newCol.data();
|
||||
if(newCol.id === col.id) return false;
|
||||
|
||||
var $newLane = $newCol.closest('.kanban-lane');
|
||||
var newLane = $newLane.data('lane');
|
||||
return colRules.indexOf(newCol.type) > -1 && newLane.id === lane.id;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change column type for a card
|
||||
* 变更卡片类型
|
||||
* @param {Object} card Card object
|
||||
* @param {String} fromColType The column type before change
|
||||
* @param {String} toColType The column type after change
|
||||
* @param {String} kanbanID Kanban ID
|
||||
*/
|
||||
function changeCardColType(card, fromColType, toColType, kanbanID)
|
||||
{
|
||||
/* TODO: Post data to server on change card type 将变更卡片类型操作提交到服务器 */
|
||||
console.log('TODO: Post data to server on change card type 将变更卡片类型操作提交到服务器', {card, fromColType, toColType, kanbanID});
|
||||
|
||||
/*
|
||||
// TODO: The server must return a updated kanban data 服务器返回更新后的看板数据
|
||||
|
||||
// 调用 updateKanban 更新看板数据
|
||||
updateKanban(kanbanID, newKanbanData);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle finish drop task
|
||||
* @param {Object} event Event object
|
||||
* @returns {void}
|
||||
*/
|
||||
function handleFinishDrop(event)
|
||||
{
|
||||
var $card = $(event.element); // The drag card
|
||||
var $dragCol = $card.closest('.kanban-lane-col');
|
||||
var $dropCol = $(event.target);
|
||||
|
||||
/* Get d-n-d(drag and drop) infos 获取拖放操作相关信息 */
|
||||
var card = $card.data('item');
|
||||
var fromColType = $dragCol.data('type');
|
||||
var toColType = $dropCol.data('type');
|
||||
var kanbanID = $card.closest('.kanban').data('id');
|
||||
|
||||
changeCardColType(card, fromColType, toColType, kanbanID);
|
||||
}
|
||||
|
||||
$(function()
|
||||
{
|
||||
var kanbanGroup = window.kanbanGroup;
|
||||
if(!kanbanGroup) return;
|
||||
|
||||
$('#kanban').kanban(
|
||||
{
|
||||
data: processKanbanData(),
|
||||
// noLaneName: true,
|
||||
droppable:
|
||||
{
|
||||
selector: '.kanban-item:not(.kanban-item-span)',
|
||||
target: findDropColumns,
|
||||
finish: handleFinishDrop,
|
||||
mouseButton: 'left'
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -9,94 +9,29 @@
|
||||
*/
|
||||
?>
|
||||
<?php include '../../common/view/header.html.php';?>
|
||||
<div id="kanban" class="main-table fade auto-fade-in" data-ride="table" data-checkable="false" data-group="true">
|
||||
<?php if(empty($kanbanGroup)):?>
|
||||
<div class="table-empty-tip">
|
||||
<p>
|
||||
<span class="text-muted"><?php echo $lang->execution->noExecutions;?></span>
|
||||
</p>
|
||||
<?php include '../../common/view/kanban.html.php';?>
|
||||
<?php if(empty($kanbanGroup)):?>
|
||||
<div class="table-empty-tip cell">
|
||||
<p class="text-muted"><?php echo $lang->execution->noExecutions;?></p>
|
||||
</div>
|
||||
<?php else:?>
|
||||
<div class='panel kanban-panel' id='kanbanList'>
|
||||
<div class='panel-body'>
|
||||
<div id='kanban' class='kanban'></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php else:?>
|
||||
<table class="table no-margin table-grouped text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class='projectColor'></th>
|
||||
<th><?php echo $lang->execution->doingProject . ' <span class="count">' . $projectCount . '</span>';?></th>
|
||||
<?php foreach($lang->execution->kanbanColType as $status => $colName):?>
|
||||
<th><?php echo $colName . ($status != 'closed' ? ' <span class="count">' . $statusCount[$status] . '</span>' : '');?></th>
|
||||
<?php endforeach;?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php $rowIndex = 0;?>
|
||||
<?php foreach($kanbanGroup as $projectID => $executionList):?>
|
||||
<tr>
|
||||
<td style="background: <?php echo $lang->execution->boardColorList[$rowIndex];?>;"></td>
|
||||
<td class='board-project'>
|
||||
<div data-id='<?php echo $projectID;?>'>
|
||||
<div class='text-center'>
|
||||
<?php $projectTitle = empty($projectID) ? $lang->execution->myExecutions : zget($projects, $projectID);?>
|
||||
<span class='group-title' title='<?php echo $projectTitle;?>'><?php echo $projectTitle;?></span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class='c-boards no-padding text-left' colspan='4'>
|
||||
<div class="boards-wrapper">
|
||||
<div class="boards">
|
||||
<?php foreach($lang->execution->kanbanColType as $colStatus => $colName):?>
|
||||
<div class="board s-<?php echo $colStatus?>">
|
||||
<div>
|
||||
<?php if(!empty($executionList[$colStatus])):?>
|
||||
<?php foreach($executionList[$colStatus] as $execution):?>
|
||||
<div class='board-item' <?php if($execution->status == 'doing' and isset($execution->delay)) echo "style='border-left: 3px solid red';";?>>
|
||||
<div class='table-row'>
|
||||
<div class='table-col'>
|
||||
<?php
|
||||
$executionName = empty($projectID) ? zget($projects, $execution->project) . ' / ' . $execution->name : $execution->name;
|
||||
|
||||
if(common::hasPriv('execution', 'task'))
|
||||
{
|
||||
echo html::a($this->createLink('execution', 'task', "executionID=$execution->id"), $executionName, '', "title='{$executionName}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<span title='{$executionName}'>{$executionName}</span>";
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php if($colStatus == 'doing'):?>
|
||||
<div class='table-col'>
|
||||
<div class="c-progress">
|
||||
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($execution->hours->progress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
|
||||
<div class='progress-info'><?php echo round($execution->hours->progress);?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach?>
|
||||
<?php endif?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach;?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php $rowIndex++; ?>
|
||||
<?php if($rowIndex == 8) $rowIndex = 0;?>
|
||||
<?php endforeach;?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
<style>
|
||||
<?php
|
||||
foreach(array_keys($lang->execution->kanbanColType) as $status)
|
||||
{
|
||||
echo ".s-$status .board-item {border-left: 3px solid {$lang->execution->statusColorList[$status]};}";
|
||||
}
|
||||
$userPrivs = array();
|
||||
$userPrivs['project'] = common::hasPriv('project', 'index');
|
||||
$userPrivs['execution'] = common::hasPriv('execution', 'task');
|
||||
js::set('userPrivs', $userPrivs);
|
||||
js::set('kanbanGroup', $kanbanGroup);
|
||||
js::set('kanbanColumns', $lang->execution->kanbanColType);
|
||||
js::set('statusColorList', $lang->execution->statusColorList);
|
||||
js::set('langMyExecutions', $lang->execution->myExecutions);
|
||||
js::set('langDoingProject', $lang->execution->doingProject);
|
||||
js::set('projectNames', $projects);
|
||||
?>
|
||||
</style>
|
||||
<?php endif;?>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user