Merge branch 'sprint/168_xiawenlong' into sprint/168

This commit is contained in:
孙广明
2021-10-27 14:51:12 +08:00
16 changed files with 352 additions and 33 deletions
+1 -2
View File
@@ -229,7 +229,7 @@ class html extends baseHTML
/**
* Create user avatar.
*
* @param string|object|array $user User object or user avatar url or user account
* @param string|object|array $user User object or user account
* @param string|int $size Avatar size, can be a number or preset sizes: "xs", "sm", "", "lg", "xl", default is ""
* @param string $className Avatar element class name, default is "avatar-circle"
* @param string $attrib Extra attributes on avatar element
@@ -248,7 +248,6 @@ class html extends baseHTML
if(is_string($user))
{
$userObj->account = $user;
if(strlen($user) > 1) $userObj->avatar = $user;
$user = $userObj;
}
elseif(is_array($user))
+6 -5
View File
@@ -1,8 +1,4 @@
#header {position: fixed; top: 0; left: 0; right: 0; z-index: 10;}
#mainMenu {position: fixed; padding: 10px 15px; top: 60px; left: 0; right: 0; background-color: #efefef; z-index: 0;}
#main {padding-bottom: 0; padding-top: 50px;}
#main > .container {max-width: none!important; padding: 0}
#kanbanContainer {border-radius: 0; margin: 48px 0 0 0;}
#main > .container {max-width: 1960px!important; padding: 0 10px}
.kanban + .kanban {margin-top: 15px}
.kanban-item > .title { display: block;white-space: nowrap; overflow: hidden; text-overflow: ellipsis}
@@ -16,3 +12,8 @@
.kanban-affixed .kanban-header-col > .title > .text,
.kanban-affixed .kanban-header-col > .title > .count {color: #fff!important;}
.dropdown-menu a {text-align: left;}
.panel{ position: unset }
.menu-actions { display: contents }
.scrollbar-hover {max-height: 2000px; overflow: scroll !important;}
+144 -10
View File
@@ -450,7 +450,7 @@ 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>')
$title = $('<a class="title iframe"><i class="icon icon-checked text-muted"></i> <span class="text"></span></a>')
.attr('href', $.createLink('task', 'view', 'taskID=' + item.id));
$title.appendTo($item);
}
@@ -464,7 +464,7 @@ function renderTaskItem(item, $item, col)
$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>',
'<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));
@@ -474,7 +474,7 @@ function renderTaskItem(item, $item, col)
return $item;
}
 
/* Add column renderer/ 添加特定列类型或列条目类型渲染方法 */
addColumnRenderer('story', renderStoryItem);
addColumnRenderer('bug', renderBugItem);
@@ -519,10 +519,137 @@ 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 = $('<div id="kanban-' + kanbanID + '" data-id="' + kanbanID + '"></div>').appendTo('#kanbans');
$kanban.kanban($.extend({data: data}, options));
}
function fullScreen()
{
var element = document.getElementById('kanbanContainer');
var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullScreen;
if(requestMethod)
{
var afterEnterFullscreen = function()
{
$('#kanbanContainer').addClass('scrollbar-hover');
$.cookie('isFullScreen', 1);
}
var whenFailEnterFullscreen = function()
{
$.cookie('isFullScreen', 0);
}
try
{
var result = requestMethod.call(element);
if(result && (typeof result.then === 'function' || result instanceof window.Promise))
{
result.then(afterEnterFullscreen).catch(whenFailEnterFullscreen);
}
else
{
afterEnterFullscreen();
}
}
catch (error)
{
whenFailEnterFullscreen(error);
}
}
}
window.kanbanDropRules =
{
story:
{
blacklog: true,
ready: ['blacklog', 'dev-doing'],
'dev-doing': ['dev-done'],
'dev-done': ['test-doing'],
'test-doing': ['test-done'],
'test-done': ['accepted'],
'accepted': ['published'],
'published': false,
}
}
/**
<<<<<<< module/execution/js/kanban.js
* Exit full screen.
*
* @access public
* @return void
*/
function exitFullScreen()
{
$('#kanbanContainer').removeClass('scrollbar-hover');
$('#content .actions').removeClass('hidden');
$.cookie('isFullScreen', 0);
}
* 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 kanbanID = $root.data('id');
var kanbanRules = window.kanbanDropRules ? window.kanbanDropRules[kanbanID] : null;
if(!kanbanRules) return $root.find('.kanban-lane-col:not([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');
var canDropHere = colRules.indexOf(newCol.type) > -1 && newLane.id === lane.id;
if(canDropHere) $newCol.addClass('can-drop-here');
return canDropHere;
});
}
/**
* Handle finish drop task
* @param {Object} event Event object
* @returns {void}
*/
function handleFinishDrop(event)
{
var $item = $(event.element); // The drag item
var $dragCol = $item.closest('.kanban-lane-col');
var $dropCol = $(event.target);
/* Get d-n-d(drag and drop) infos 获取拖放操作相关信息 */
var item = $item.data('item');
var fromColType = $dragCol.data('type');
var toColType = $dropCol.data('type');
var kanbanID = $item.closest('.kanban').data('id');
/* TODO: Save d-n-d infos to server 将拖放操作信息提交到服务器 */
console.log('TODO: Save d-n-d infos to server 将拖放操作信息提交到服务器', {item, fromColType, toColType, kanbanID});
/*
// TODO: The server must return a updated kanban data 服务器返回更新后的看板数据
// 调用 updateKanban 更新看板数据
updateKanban(kanbanID, newKanbanData);
*/
$('#kanbans').find('.can-drop-here').removeClass('can-drop-here');
}
/* Overload kanban default options */
$.extend($.fn.kanban.Constructor.DEFAULTS,
{
@@ -543,12 +670,19 @@ $(function()
/* Common options 用于初始化看板的通用选项 */ 
var commonOptions =
{
maxColHeight: 'auto',
minColWidth: 240,
droppable: true,
showCount: true,
showZeroCount: true,
countRender: renderColumnCount
maxColHeight: 'auto',
minColWidth: 240,
maxColWidth: 240,
showCount: true,
showZeroCount: true,
fluidBoardWidth: true,
countRender: renderColumnCount,
droppable:
{
target: findDropColumns,
finish: handleFinishDrop,
mouseButton: 'left'
}
};
/* Create story kanban 创建需求看板 */
+1
View File
@@ -379,6 +379,7 @@ $lang->execution->kanban = "看板";
$lang->execution->kanbanSetting = "看板设置";
$lang->execution->resetKanban = "恢复默认";
$lang->execution->printKanban = "打印看板";
$lang->execution->fullScreen = "看板全屏展示";
$lang->execution->bugList = "Bug列表";
$lang->execution->kanbanHideCols = '看板隐藏已关闭、已取消列';
+12 -4
View File
@@ -13,14 +13,12 @@
<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'");?>
<?php if(common::hasPriv('execution', 'printKanban')) echo html::a($this->createLink('execution', 'printKanban', "executionID=$executionID"), "<i class='icon-printer muted'></i> " . $lang->execution->printKanban, '', "class='iframe btn btn-link' id='printKanban' title='{$lang->execution->printKanban}' data-width='500'");?>
<?php
$link = $this->createLink('task', 'export', "execution=$executionID&orderBy=$orderBy&type=kanban");
if(common::hasPriv('task', 'export')) echo html::a($link, "<i class='icon-export muted'></i> " . $lang->task->export, '', "class='btn btn-link iframe export' data-width='700'");
if(common::hasPriv('task', 'export')) echo html::a($link, "<i class='icon-export muted'></i> " . $lang->export, '', "class='btn btn-link iframe export' data-width='700'");
?>
<?php if($canBeChanged):?>
<div class='btn-group'>
<div class='btn-group' style="margin-right: 0">
<button type='button' class='btn btn-link dropdown-toggle' data-toggle='dropdown' id='importAction'>
<i class='icon-import muted'></i> <?php echo $lang->import ?>
<span class='caret'></span>
@@ -37,6 +35,16 @@
?>
</ul>
</div>
<?php
echo "<div class='btn-group menu-actions'>";
echo html::a('javascript:;', "<i class='icon icon-ellipsis-v'></i>", '', "data-toggle='dropdown' class='btn btn-link'");
// TODO 暂时解决样式问题
echo "<ul class='dropdown-menu pull-right' style='position: relative'>";
if(common::hasPriv('execution', 'printKanban')) echo '<li>' .html::a($this->createLink('execution', 'printKanban', "executionID=$executionID"), "<i class='icon-printer muted'></i> " . $lang->execution->printKanban, '', "class='iframe btn btn-link' id='printKanban' title='{$lang->execution->printKanban}' data-width='500'") . '</li>';
echo '<li>' .html::a('javascript:fullScreen()', "<i class='icon-fullscreen muted'></i> " . $lang->execution->fullScreen, '', "class='btn btn-link' title='{$lang->execution->fullScreen}' data-width='500'") . '</li>';
echo '</ul></div>';
?>
<?php
$checkObject = new stdclass();
$checkObject->execution = $executionID;
+6 -2
View File
@@ -2,8 +2,10 @@
global $lang;
$config->kanban = new stdclass();
$config->kanban->setwip = new stdclass();
$config->kanban->setwip->requiredFields = 'limit';
$config->kanban->setwip = new stdclass();
$config->kanban->setlane = new stdclass();
$config->kanban->setwip->requiredFields = 'limit';
$config->kanban->setlane->requiredFields = 'name,type';
$config->kanban->default = new stdclass();
$config->kanban->default->story = new stdclass();
@@ -64,3 +66,5 @@ $config->kanban->taskColumnStatusList['developed'] = 'done';
$config->kanban->taskColumnStatusList['pause'] = 'pause';
$config->kanban->taskColumnStatusList['canceled'] = 'cancel';
$config->kanban->taskColumnStatusList['closed'] = 'closed';
$config->kanban->laneColorList = array('#7ec5ff', '#333', '#2b529c', '#e48600', '#d2323d', '#229f24', '#777', '#d2691e', '#008b8b', '#2e8b57', '#4169e1', '#4b0082', '#fa8072', '#ba55d3', '#2e8b57', '#6b8e23');
+30 -1
View File
@@ -27,7 +27,7 @@ class kanban extends control
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->loadModel('action')->create('wip', $columnID, 'Edited');
die(js::reload('parent.parent'));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
}
$this->app->loadLang('story');
@@ -43,4 +43,33 @@ class kanban extends control
$this->view->status = $status;
$this->display();
}
/**
* Set lane info.
*
* @param int $laneID
* @param int $executionID
* @access public
* @return void
*/
public function setLane($laneID, $executionID = 0)
{
if($_POST)
{
$this->kanban->setLane($laneID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->loadModel('action')->create('lane', $laneID, 'Edited');
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
}
$lane = $this->kanban->getLaneById($laneID);
if(!$lane) die(js::error($this->lang->notFound) . js::locate($this->createLink('execution', 'kanban', "executionID=$executionID")));
$this->view->title = zget($this->lang->kanban->laneTypeList, $lane->type) . $this->lang->colon . $this->lang->kanban->setLane;
$this->view->lane = $lane;
$this->display();
}
}
+4
View File
@@ -0,0 +1,4 @@
td>ul {padding-left: 0;}
li {display: block; float: left; padding: 5px; width: 28px; height: 28px;}
li>a { position: relative; display: block; width: 100%; height: 100%; padding: 0; margin: 0; font-family: ZentaoIcon; font-size: 14px; font-style: normal; font-weight: 400; font-variant: normal; line-height: 1; text-align: center; text-transform: none; border: 1px solid transparent; border-radius: 50%; speak: none; -webkit-font-smoothing: antialiased; }
li>a.active:before {font-size: 14px; content: "\e5ca"}
+13
View File
@@ -0,0 +1,13 @@
/**
* Set lane color.
*
* @param string $color
* @access public
* @return void
*/
function setColor(color)
{
$('.cp-tile').removeClass('active');
$('.cp-tile[data-color="' + color + '"]').addClass('active');
$('#color').val(color);
}
+22
View File
@@ -1,6 +1,22 @@
<?php
$lang->kanban = new stdClass();
$lang->kanban->WIP = 'WIP';
$lang->kanban->setWIP = 'WIP Settings';
$lang->kanban->WIPStatus = 'WIP Status';
$lang->kanban->WIPStage = 'WIP Stage';
$lang->kanban->WIPType = 'WIP Type';
$lang->kanban->WIPCount = 'WIP Count';
$lang->kanban->noLimit = 'No Limit ∞';
$lang->kanban->setLane = 'Lane Settings';
$lang->kanban->laneName = 'Lane Name';
$lang->kanban->laneColor = 'Lane Color';
$this->lang->kanban->laneTypeList = array();
$this->lang->kanban->laneTypeList['story'] = $lang->SRCommon;
$this->lang->kanban->laneTypeList['bug'] = 'Bug';
$this->lang->kanban->laneTypeList['task'] = 'Task';
$lang->kanban->storyColumn = array();
$lang->kanban->storyColumn['backlog'] = 'Backlog';
$lang->kanban->storyColumn['ready'] = 'Ready';
@@ -33,3 +49,9 @@ $lang->kanban->taskColumn['developed'] = 'Developed';
$lang->kanban->taskColumn['pause'] = 'Pause';
$lang->kanban->taskColumn['canceled'] = 'Canceled';
$lang->kanban->taskColumn['closed'] = 'Closed';
$lang->kanbancolumn = new stdclass();
$lang->kanbancolumn->limit = $lang->kanban->WIPCount;
$lang->kanbanlane = new stdclass();
$lang->kanbanlane->name = $lang->kanban->laneName;
+15
View File
@@ -5,8 +5,17 @@ $lang->kanban->WIP = 'WIP';
$lang->kanban->setWIP = '在制品设置';
$lang->kanban->WIPStatus = '在制品状态';
$lang->kanban->WIPStage = '在制品阶段';
$lang->kanban->WIPType = '在制品类型';
$lang->kanban->WIPCount = '在制品数量';
$lang->kanban->noLimit = '不限制∞';
$lang->kanban->setLane = '泳道设置';
$lang->kanban->laneName = '泳道名称';
$lang->kanban->laneColor = '泳道颜色';
$this->lang->kanban->laneTypeList = array();
$this->lang->kanban->laneTypeList['story'] = $lang->SRCommon;
$this->lang->kanban->laneTypeList['bug'] = 'Bug';
$this->lang->kanban->laneTypeList['task'] = '任务';
$lang->kanban->storyColumn = array();
$lang->kanban->storyColumn['backlog'] = 'Backlog';
@@ -40,3 +49,9 @@ $lang->kanban->taskColumn['developed'] = '研发完毕';
$lang->kanban->taskColumn['pause'] = '已暂停';
$lang->kanban->taskColumn['canceled'] = '已取消';
$lang->kanban->taskColumn['closed'] = '已关闭';
$lang->kanbancolumn = new stdclass();
$lang->kanbancolumn->limit = $lang->kanban->WIPCount;
$lang->kanbanlane = new stdclass();
$lang->kanbanlane->name = $lang->kanban->laneName;
+35 -5
View File
@@ -60,7 +60,7 @@ class kanbanModel extends model
}
$kanban = array();
foreach($lanes as $laneID => $lane)
foreach($lanes as $laneID => $lane)
{
$lane->columns = $columns[$laneID];
$kanban[$lane->type] = $lane;
@@ -308,6 +308,18 @@ class kanbanModel extends model
return $column;
}
/**
* Get lane by id.
*
* @param int $laneID
* @access public
* @return object
*/
public function getLaneById($laneID)
{
return $this->dao->findById($laneID)->from(TABLE_KANBANLANE)->fetch();
}
/**
* Set WIP limit.
*
@@ -319,19 +331,37 @@ class kanbanModel extends model
{
$column = $this->getColumnById($columnID);
$data = fixer::input('post')
->cleanInt('limit')
->remove('WIPCount,noLimit')
->get();
$this->lang->kanbancolumn = new stdclass();
$this->lang->kanbancolumn->limit = $this->lang->kanban->WIPCount;
$this->dao->update(TABLE_KANBANCOLUMN)->data($data)
->autoCheck()
->check(is_numeric($data->limit), 'limit', 'gt', 0)
->check('limit', 'gt', 0)
->batchcheck($this->config->kanban->setwip->requiredFields, 'notempty')
->where('id')->eq($columnID)
->exec();
return dao::isError();
}
/**
* Set lane info.
*
* @param int $laneID
* @access public
* @return bool
*/
public function setLane($laneID)
{
$lane = fixer::input('post')->get();
$this->dao->update(TABLE_KANBANLANE)->data($lane)
->autoCheck()
->batchcheck($this->config->kanban->setlane->requiredFields, 'notempty')
->where('id')->eq($laneID)
->exec();
return dao::isError();
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
/**
* The set lane file of task module of ZenTaoPMS.
*
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Yuchun Li <liyuchun@easycorp.ltd>
* @package kanban
* @version $Id: setlane.html.php 935 2021-10-26 16:24:24Z liyuchun@easycorp.ltd $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.lite.html.php';?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2>
<?php echo "<span title='$title'>" . $title . '</span>';?>
</h2>
</div>
<form class="load-indicator main-form form-ajax" method='post' target='hiddenwin'>
<table align='center' class='table table-form'>
<tr>
<th><?php echo $lang->kanban->laneName;?></th>
<td colspan='2'>
<?php echo html::input('name', $lane->name, "class='form-control'");?>
</td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->kanban->WIPType;?></th>
<td colspan='2'>
<?php echo html::input('type', zget($lang->kanban->laneTypeList, $lane->type), "class='form-control' disabled");?>
</td>
</tr>
<tr>
<th><?php echo $lang->kanban->laneColor;?></th>
<td colspan='3'>
<?php echo html::hidden('color', $lane->color, "class='form-control'");?>
<ul>
<?php foreach($config->kanban->laneColorList as $color):?>
<li>
<a href='javascript:setColor("<?php echo $color;?>");' class='cp-tile <?php echo $color == $lane->color ? 'active' : '';?>' data-color='<?php echo $color;?>' style='color: #FFF; background: <?php echo $color;?>; border-color: transparent;'></a>
</li>
<?php endforeach;?>
</ul>
</td>
</tr>
<tr>
<td colspan='4' class='text-center form-actions'>
<?php echo html::submitButton();?>
</td>
</tr>
</table>
</form>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
+1
View File
@@ -868,6 +868,7 @@ class projectModel extends model
$this->dao->insert(TABLE_PRODUCT)->data($product)->exec();
$productID = $this->dao->lastInsertId();
$this->loadModel('action')->create('product', $productID, 'opened');
$this->dao->update(TABLE_PRODUCT)->set('`order`')->eq($productID * 5)->where('id')->eq($productID)->exec();
if($product->acl != 'open') $this->loadModel('user')->updateUserView($productID, 'product');
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long