* task #44357, refactor executions kanban with kanban component and support dnd.

This commit is contained in:
Hao Sun
2021-11-17 17:51:20 +08:00
parent 94cd6525e4
commit 6ad007f06e
5 changed files with 189 additions and 114 deletions
+27 -3
View File
@@ -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)
{
+7 -24
View File
@@ -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;}
+133
View File
@@ -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'
},
});
});
+21 -86
View File
@@ -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