From b9190b8d9250d79d446b85e7521d96b5683f4cee Mon Sep 17 00:00:00 2001 From: tianshujie Date: Tue, 30 Aug 2022 15:14:36 +0800 Subject: [PATCH 1/3] * Solve bug link bug search all products reported errors. --- module/bug/model.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/module/bug/model.php b/module/bug/model.php index 66472d082b..9730388210 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -2935,7 +2935,21 @@ class bugModel extends model /* If search criteria don't have products, append the selected product from the top left dropdown-menu. */ if(is_array($productIDList)) $productIDList = implode(',', $productIDList); - if(strpos($bugQuery, '`product`') === false) $bugQuery .= ' AND `product` IN (' . $productIDList . ')'; + if(strpos($bugQuery, '`product`') === false) + { + $bugQuery .= ' AND `product` IN (' . $productIDList . ')'; + } + else + { + $productParis = $this->loadModel('product')->getPairs(); + $productIDList = array_keys($productParis); + + if(!empty($productIDList)) + { + $productIDList = implode(',', $productIDList); + $bugQuery .= ' AND `product` IN (' . $productIDList . ')'; + } + } $allBranch = "`branch` = 'all'"; if($branch !== 'all' and strpos($bugQuery, '`branch` =') === false) $bugQuery .= " AND `branch` in('0','$branch')"; From 8077fe8a3451afbc75340ba568a666e110effcc6 Mon Sep 17 00:00:00 2001 From: tianshujie Date: Wed, 31 Aug 2022 15:39:53 +0800 Subject: [PATCH 2/3] * Finish task #66996. --- module/kanban/control.php | 21 ++++- module/kanban/css/view.css | 22 ++++- module/kanban/js/common.js | 13 +++ module/kanban/js/view.js | 140 +++++++++++++++++++++++++++++++ module/kanban/lang/de.php | 1 + module/kanban/lang/en.php | 1 + module/kanban/lang/fr.php | 1 + module/kanban/lang/zh-cn.php | 1 + module/kanban/view/view.html.php | 134 ++++++++++++++++++++--------- 9 files changed, 288 insertions(+), 46 deletions(-) mode change 100755 => 100644 module/kanban/view/view.html.php diff --git a/module/kanban/control.php b/module/kanban/control.php index e58bca853b..ead87199bf 100644 --- a/module/kanban/control.php +++ b/module/kanban/control.php @@ -389,11 +389,14 @@ class kanban extends control * View a kanban. * * @param int $kanbanID + * @param int $regionID * @access public * @return void */ - public function view($kanbanID) + public function view($kanbanID, $regionID = 'all') { + $this->session->set('kanbanView', $this->app->getURI(true), 'kanban'); + $kanban = $this->kanban->getByID($kanbanID); $users = $this->loadModel('user')->getPairs('noletter|nodeleted'); @@ -419,11 +422,14 @@ class kanban extends control $userList[$account]['avatar'] = $avatar; } + $regions = $this->kanban->getKanbanData($kanbanID); + $this->view->users = $users; $this->view->title = $this->lang->kanban->view; - $this->view->regions = $this->kanban->getKanbanData($kanbanID); $this->view->userList = $userList; $this->view->kanban = $kanban; + $this->view->regions = $regions; + $this->view->regionID = isset($regions[$regionID]) ? $regionID : 'all'; $this->display(); } @@ -469,7 +475,14 @@ class kanban extends control $regionID = $this->kanban->createRegion($kanban, '', $copyRegionID, $from); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent')); + if($from == 'kanban') + { + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'callback' => "parent.loadKanban($kanbanID, $regionID)")); + } + else + { + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent')); + } } $regions = $this->kanban->getRegionPairs($kanbanID, 0, $from); @@ -935,7 +948,7 @@ class kanban extends control { $this->kanban->batchCreateCard($kanbanID, $regionID, $groupID, $columnID); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $backLink)); + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->session->kanbanView)); } $kanbanUsers = $kanbanID == 0 ? ',' : trim($kanban->owner) . ',' . trim($kanban->team); diff --git a/module/kanban/css/view.css b/module/kanban/css/view.css index d024f92111..d7c572ea17 100644 --- a/module/kanban/css/view.css +++ b/module/kanban/css/view.css @@ -13,7 +13,7 @@ #kanbanContainer.fullscreen {overflow: auto;} .region {border: 0px solid #dcdcdc; background-color: #fff;} -.region + .region {margin-top: 20px;} +.region + .region {margin-top: 10px;} .region .region-header {padding: 10px;} .region .region-header strong {font-size: 14px; color: #3C4353; float: left;} .region .region-header label {color: #999; background: transparent; border: 1px solid #ddd; margin-left: 10px; margin-right: 10px} @@ -156,3 +156,23 @@ .region .kanban-board .kanban-header-sub-cols .kanban-header-col > .title > span {max-width: calc(100% - 165px) !important;} .region .kanban-board .kanban-header-sub-cols .kanban-header-col.left > .title > span {max-width: calc(100% - 120px) !important;} .executionName .delayed {margin-right: 10px;} + +#kanbanBox {position: relative;} +#regionTabs {background-color: #efefef; z-index: 100; display: flex;} +#regionTabs .leftBtn.disabled i, #regionTabs .rightBtn.disabled i{color: #c2c2c2;} +#regionTabs .icon-angle-left, #regionTabs .icon-angle-right {font-size: 20px; vertical-align: -webkit-baseline-middle;} +#regionNavTabs {display: inline-flex; overflow: hidden; width: 100%;} +#regionNavTabs > ul {display: inline-flex; border-bottom: unset;} +#regionNavTabs > ul > li {padding: 6px 15px; margin-bottom: 6px; border-radius: 4px 4px 0px 0px;} +#regionNavTabs > ul > li.active {background-color: #fff;} +#regionNavTabs > ul > li.active::before {content: ''; width: 10px; height: 10px; position: absolute; left: -10px; top: 21px; background: radial-gradient(circle at 0% 0%,transparent 10px,#fff 0);} +#regionNavTabs > ul > li.active::after {content: ''; width: 10px; height: 10px; position: absolute; left: calc(100%); top: 21px; background: radial-gradient(circle at 100% 0%,transparent 10px,#fff 0);} +#regionNavTabs > ul > li > a {max-width: 195px; overflow: hidden; white-space: nowrap; padding: 0;} +#regionNavTabs > ul > li.active > a {color: #0c64eb !important;} +#regionNavTabs > ul > li.active > a:before {background: unset;} +#regionTabs.affixed {margin-bottom: 5px; position: fixed; top: 0px;} +#regionTabs.affixed + #kanbanContainer .kanban-board.kanban-affixed > .kanban-header {margin-top: 30px;} +#regionTabs.affixed + #kanbanContainer .kanban-board.kanban-affixed > .kanban-header + .kanban-lane {margin-top: 38px;} +#regionTabs.affixed ul > li {margin-bottom: 0px !important;} +#regionTabs .region-actions .dropdown-menu {top: 25px;} +#region-tab-actions {display: flex; position: relative; right: 0px;} diff --git a/module/kanban/js/common.js b/module/kanban/js/common.js index 3c124d5671..4584b28e30 100644 --- a/module/kanban/js/common.js +++ b/module/kanban/js/common.js @@ -148,3 +148,16 @@ function loadOwners(spaceID) $('#owner').chosen(); }); } + +/** + * Load kanban. + * + * @param int $kanbanID + * @param int $regionID + * @access public + * @return void + */ +function loadKanban(kanbanID, regionID) +{ + location.href = createLink('kanban', 'view', 'kanbanID=' + kanbanID + '®ionID=' + regionID); +} diff --git a/module/kanban/js/view.js b/module/kanban/js/view.js index 5bec4a50a1..b532528fd3 100644 --- a/module/kanban/js/view.js +++ b/module/kanban/js/view.js @@ -1508,6 +1508,7 @@ $(function() $(window).on('scroll', function() { $.zui.ContextMenu.hide(); + if($('#regionTabs').length > 0) updateRegionTabAffixState(); }); $(document).on('click', '#splitTable .btn-plus', function() @@ -1547,8 +1548,28 @@ $(function() if(!CRKanban && kanbanInfo.status == 'closed') $('.kanban-col.kanban-header-col').css('padding', '0px 0px 0px 0px'); setToolTip(); + + $(window).on('resize', initRegionTabs); + + $('.leftBtn').click(function() + { + if($(this).hasClass('disabled')) return; + swipeRegionNavTabs($('#regionNavTabs').find('ul'), 'left'); + }); + + $('.rightBtn').click(function() + { + if($(this).hasClass('disabled')) return; + swipeRegionNavTabs($('#regionNavTabs').find('ul'), 'right'); + }); }); +/** + * Init sortable. + * + * @access public + * @return void + */ function initSortable() { var sortType = ''; @@ -1777,3 +1798,122 @@ function setToolTip() { $('[data-toggle="tooltip"]').tooltip({container: 'body'}); } + +/** + * Update kanban affix state for all boards in page. + * + * @access public + * @return void + */ +function updateRegionTabAffixState() +{ + var $kanbanContainer = $('#kanbanContainer'); + var kanbanContainer = $kanbanContainer[0].getBoundingClientRect(); + var $regionTabs = $('#regionTabs'); + var regionTabs = $regionTabs[0].getBoundingClientRect(); + if(regionTabs.top <= 0 && !$regionTabs.hasClass('affixed')) + { + $regionTabs.addClass('affixed'); + $regionTabs.find('#region-tab-actions').addClass('hidden'); + } + else if($regionTabs.hasClass('affixed') && kanbanContainer.top >= 0) + { + $regionTabs.removeClass('affixed'); + $regionTabs.find('#region-tab-actions').removeClass('hidden'); + } + + initRegionTabs(); +} + +/** + * Swipe region navigation tabs. + * + * @param object $object + * @param string $direction + * @access public + * @return void + */ +function swipeRegionNavTabs($object, direction) +{ + var $regionNavTabs = $('#regionNavTabs'); + var offsetWidth = $regionNavTabs[0].offsetWidth; + var objectWidth = $object[0].offsetWidth; + var radius = 0; + + $object.css('transition-duration', '1s'); + + $object.find('li').each(function() + { + if($(this).hasClass('active') && radius == 0) radius = radiusWidth; + + /* Get the offset of the item. */ + var itemLeft = $(this)[0].offsetLeft; + var itemWidth = $(this)[0].offsetWidth; + var itemOffset = itemLeft + itemWidth; + + /* Calculate the offset after sliding. */ + if(direction == 'left' && (itemOffset + distance + radius) >= 0) + { + /* If you swipe left, the distance is equal to the item's left. */ + distance = - itemLeft - radius; + if(distance + radius >= 0) + { + distance = radius; + $('.leftBtn').addClass('disabled'); + } + $object[0].style.transform = 'translateX('+ distance +'px)'; + + /* If the width of regionNavTabs plus offsetWidth is less than the width of object, change rightBtn to clickable. */ + if(offsetWidth - distance < objectWidth) $('.rightBtn').removeClass('disabled'); + return false; + } + + if(direction == 'right' && itemOffset > (offsetWidth - distance + radius)) + { + /* If you swipe right, the distance is equal to the left distance of item plus the width of item minus the width of the regionNavTabs. */ + distance = offsetWidth - itemOffset - radius; + if($(this).next().length == 0) + { + distance = - objectWidth + offsetWidth - radius; + $('.rightBtn').addClass('disabled'); + } + $object[0].style.transform = 'translateX('+ distance +'px)'; + + /* If distance is less than 0, change leftBtn to clickable. */ + if(distance < 0) $('.leftBtn').removeClass('disabled'); + return false; + } + }); +} + +/** + * Init region tabs. + * + * @access public + * @return void + */ +function initRegionTabs() +{ + var $regionNavTabs = $('#regionNavTabs'); + if($regionNavTabs.length == 0) return; + + /* Set the width of regionTab. */ + $('#regionTabs').width($('#kanban').outerWidth()); + + var regionTabsWidth = $regionNavTabs[0].offsetWidth; + var regionTabULWidth = $regionNavTabs.find('ul')[0].offsetWidth; + var $acitiveItem = $('#regionNavTabs > ul > li.active'); + var acitiveItemWidth = $acitiveItem[0].offsetWidth; + var acitiveItemLeft = $acitiveItem[0].offsetLeft; + + /* Print left and right button. */ + if(regionTabULWidth > regionTabsWidth) $('.leftBtn, .rightBtn').removeClass('hidden'); + + /* Locate the position of the currently selected item. */ + radiusWidth = 10; + distance = (acitiveItemLeft + acitiveItemWidth) > regionTabsWidth ? - (acitiveItemLeft + acitiveItemWidth - regionTabsWidth + radiusWidth) : 0; + if($acitiveItem.prev().length == 0) distance = radiusWidth; + $regionNavTabs.find('ul')[0].style.transform = 'translateX('+ distance +'px)'; + if(distance < 0) $('#regionTabs').find('.leftBtn').removeClass('disabled'); + if($acitiveItem.next().length != 0 && regionTabULWidth > regionTabsWidth) $('#regionTabs').find('.rightBtn').removeClass('disabled'); +} diff --git a/module/kanban/lang/de.php b/module/kanban/lang/de.php index ddf3f1c317..55016fdf0b 100644 --- a/module/kanban/lang/de.php +++ b/module/kanban/lang/de.php @@ -361,6 +361,7 @@ $lang->kanbanregion = new stdclass(); $lang->kanbanregion->name = 'Region Name'; $lang->kanbanregion->default = 'Default Region'; $lang->kanbanregion->style = 'Region Style'; +$lang->kanbanregion->all = 'All Region'; $lang->kanbanregion->confirmDelete = 'Are you sure to delete this region? After deleting this region, all data in this region will be deleted.'; diff --git a/module/kanban/lang/en.php b/module/kanban/lang/en.php index 44ef17286f..de64542c0f 100644 --- a/module/kanban/lang/en.php +++ b/module/kanban/lang/en.php @@ -361,6 +361,7 @@ $lang->kanbanregion = new stdclass(); $lang->kanbanregion->name = 'Region Name'; $lang->kanbanregion->default = 'Default Region'; $lang->kanbanregion->style = 'Region Style'; +$lang->kanbanregion->all = 'All Region'; $lang->kanbanregion->confirmDelete = 'Are you sure to delete this region? After deleting this region, all data in this region will be deleted.'; diff --git a/module/kanban/lang/fr.php b/module/kanban/lang/fr.php index f350920013..03ac3373b4 100644 --- a/module/kanban/lang/fr.php +++ b/module/kanban/lang/fr.php @@ -361,6 +361,7 @@ $lang->kanbanregion = new stdclass(); $lang->kanbanregion->name = 'Region Name'; $lang->kanbanregion->default = 'Default Region'; $lang->kanbanregion->style = 'Region Style'; +$lang->kanbanregion->all = 'All Region'; $lang->kanbanregion->confirmDelete = 'Are you sure to delete this region? After deleting this region, all data in this region will be deleted.'; diff --git a/module/kanban/lang/zh-cn.php b/module/kanban/lang/zh-cn.php index d0a83afc89..89d62c0633 100644 --- a/module/kanban/lang/zh-cn.php +++ b/module/kanban/lang/zh-cn.php @@ -361,6 +361,7 @@ $lang->kanbanregion = new stdclass(); $lang->kanbanregion->name = '区域名称'; $lang->kanbanregion->default = '默认区域'; $lang->kanbanregion->style = '区域样式'; +$lang->kanbanregion->all = '所有区域'; $lang->kanbanregion->confirmDelete = '您确认删除该区域吗?删除该区域后,该区域中所有数据将会被删除。'; diff --git a/module/kanban/view/view.html.php b/module/kanban/view/view.html.php old mode 100755 new mode 100644 index ec42b74297..38fbb1ca30 --- a/module/kanban/view/view.html.php +++ b/module/kanban/view/view.html.php @@ -45,56 +45,108 @@ js::set('mode', $config->systemMode); js::set('alignment', $kanban->alignment); js::set('priv', array('canAssignCard' => common::hasPriv('kanban', 'assigncard'))); -$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1; -$canEditRegion = commonModel::hasPriv('kanban', 'editRegion'); -$canCreateRegion = commonModel::hasPriv('kanban', 'createRegion'); -$canDeleteRegion = commonModel::hasPriv('kanban', 'deleteRegion'); -$canCreateLane = commonModel::hasPriv('kanban', 'createLane'); +$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1; +$canEditRegion = commonModel::hasPriv('kanban', 'editRegion'); +$canCreateRegion = commonModel::hasPriv('kanban', 'createRegion'); +$canDeleteRegion = commonModel::hasPriv('kanban', 'deleteRegion'); +$canCreateLane = commonModel::hasPriv('kanban', 'createLane'); +$canViewArchivedCard = commonModel::hasPriv('kanban', 'viewArchivedCard'); +$canViewArchivedColumn = commonModel::hasPriv('kanban', 'viewArchivedColumn'); ?> -
-
-
- -
-