From 3a30ffec775986efd47e1e2a2bb19fd00f07e601 Mon Sep 17 00:00:00 2001 From: xieqiyu Date: Mon, 6 Sep 2021 11:08:27 +0800 Subject: [PATCH 1/8] * Finish task#42121. --- module/my/lang/de.php | 3 +++ module/my/lang/en.php | 3 +++ module/my/lang/fr.php | 3 +++ module/my/lang/vi.php | 3 +++ module/my/lang/zh-cn.php | 3 +++ module/my/lang/zh-tw.php | 3 +++ 6 files changed, 18 insertions(+) diff --git a/module/my/lang/de.php b/module/my/lang/de.php index b70fb5fe7c..b558580030 100644 --- a/module/my/lang/de.php +++ b/module/my/lang/de.php @@ -86,18 +86,21 @@ if($config->systemMode == 'new') $lang->my->executionLink = 'Execution Default P $lang->my->programLinkList = array(); $lang->my->programLinkList['program-browse'] = 'By default, you go to the program list, where you can view all of the programs'; $lang->my->programLinkList['program-project'] = 'By default, you go to the list of items in the most recent program, and you can view all items under the current program'; +if($config->systemMode == 'new') $lang->my->programLinkList['program-kanban'] = 'Enter the Program Kanban by default, and check the progress of all Programs'; $lang->my->productLinkList = array(); $lang->my->productLinkList['product-index'] = 'The default access to the product home page, you can understand the company’s overall product status'; $lang->my->productLinkList['product-all'] = 'By default, you go to the product list, where you can view all the products'; $lang->my->productLinkList['product-dashboard'] = 'By default, go to the latest product dashboard to see the current product overview'; $lang->my->productLinkList['product-browse'] = 'By default, go to the list of requirements for the most recent product and see the requirements under the current product'; +$lang->my->productLinkList['product-kanban'] = 'Enter the Product Kanban by default, and check the progress of all Products'; global $config; $lang->my->projectLinkList = array(); $lang->my->projectLinkList['project-browse'] = 'By default, you go to the project list, where you can view all the projects'; $lang->my->projectLinkList['project-execution'] = 'Go to Project-Exection by default. You can check all information in Execution'; $lang->my->projectLinkList['project-index'] = 'By default, go to the most recent project dashboard to see the current project overview'; +if($config->systemMode == 'new') $lang->my->projectLinkList['project-kanban'] = 'Enter the Project Kanban by default, and check the progress of all Projects'; $lang->my->executionLinkList = array(); if($config->systemMode == 'new') diff --git a/module/my/lang/en.php b/module/my/lang/en.php index 5795a6a935..97b83bca18 100644 --- a/module/my/lang/en.php +++ b/module/my/lang/en.php @@ -93,18 +93,21 @@ if($config->systemMode == 'new') $lang->my->executionLink = 'Execution Default P $lang->my->programLinkList = array(); $lang->my->programLinkList['program-browse'] = 'By default, you go to the program list, where you can view all of the programs'; $lang->my->programLinkList['program-project'] = 'By default, you go to the list of items in the most recent program, and you can view all items under the current program'; +if($config->systemMode == 'new') $lang->my->programLinkList['program-kanban'] = 'Enter the Program Kanban by default, and check the progress of all Programs'; $lang->my->productLinkList = array(); $lang->my->productLinkList['product-index'] = 'The default access to the product home page, you can understand the company’s overall product status'; $lang->my->productLinkList['product-all'] = 'By default, you go to the product list, where you can view all the products'; $lang->my->productLinkList['product-dashboard'] = 'By default, go to the latest product dashboard to see the current product overview'; $lang->my->productLinkList['product-browse'] = 'By default, go to the list of requirements for the most recent product and see the requirements under the current product'; +$lang->my->productLinkList['product-kanban'] = 'Enter the Product Kanban by default, and check the progress of all Products'; global $config; $lang->my->projectLinkList = array(); $lang->my->projectLinkList['project-browse'] = 'By default, you go to the project list, where you can view all the projects'; $lang->my->projectLinkList['project-execution'] = 'Go to Project-Exection by default. You can check all information in Execution'; $lang->my->projectLinkList['project-index'] = 'By default, go to the most recent project dashboard to see the current project overview'; +if($config->systemMode == 'new') $lang->my->projectLinkList['project-kanban'] = 'Enter the Project Kanban by default, and check the progress of all Projects'; $lang->my->executionLinkList = array(); if($config->systemMode == 'new') diff --git a/module/my/lang/fr.php b/module/my/lang/fr.php index b70fb5fe7c..b558580030 100644 --- a/module/my/lang/fr.php +++ b/module/my/lang/fr.php @@ -86,18 +86,21 @@ if($config->systemMode == 'new') $lang->my->executionLink = 'Execution Default P $lang->my->programLinkList = array(); $lang->my->programLinkList['program-browse'] = 'By default, you go to the program list, where you can view all of the programs'; $lang->my->programLinkList['program-project'] = 'By default, you go to the list of items in the most recent program, and you can view all items under the current program'; +if($config->systemMode == 'new') $lang->my->programLinkList['program-kanban'] = 'Enter the Program Kanban by default, and check the progress of all Programs'; $lang->my->productLinkList = array(); $lang->my->productLinkList['product-index'] = 'The default access to the product home page, you can understand the company’s overall product status'; $lang->my->productLinkList['product-all'] = 'By default, you go to the product list, where you can view all the products'; $lang->my->productLinkList['product-dashboard'] = 'By default, go to the latest product dashboard to see the current product overview'; $lang->my->productLinkList['product-browse'] = 'By default, go to the list of requirements for the most recent product and see the requirements under the current product'; +$lang->my->productLinkList['product-kanban'] = 'Enter the Product Kanban by default, and check the progress of all Products'; global $config; $lang->my->projectLinkList = array(); $lang->my->projectLinkList['project-browse'] = 'By default, you go to the project list, where you can view all the projects'; $lang->my->projectLinkList['project-execution'] = 'Go to Project-Exection by default. You can check all information in Execution'; $lang->my->projectLinkList['project-index'] = 'By default, go to the most recent project dashboard to see the current project overview'; +if($config->systemMode == 'new') $lang->my->projectLinkList['project-kanban'] = 'Enter the Project Kanban by default, and check the progress of all Projects'; $lang->my->executionLinkList = array(); if($config->systemMode == 'new') diff --git a/module/my/lang/vi.php b/module/my/lang/vi.php index b70fb5fe7c..b558580030 100644 --- a/module/my/lang/vi.php +++ b/module/my/lang/vi.php @@ -86,18 +86,21 @@ if($config->systemMode == 'new') $lang->my->executionLink = 'Execution Default P $lang->my->programLinkList = array(); $lang->my->programLinkList['program-browse'] = 'By default, you go to the program list, where you can view all of the programs'; $lang->my->programLinkList['program-project'] = 'By default, you go to the list of items in the most recent program, and you can view all items under the current program'; +if($config->systemMode == 'new') $lang->my->programLinkList['program-kanban'] = 'Enter the Program Kanban by default, and check the progress of all Programs'; $lang->my->productLinkList = array(); $lang->my->productLinkList['product-index'] = 'The default access to the product home page, you can understand the company’s overall product status'; $lang->my->productLinkList['product-all'] = 'By default, you go to the product list, where you can view all the products'; $lang->my->productLinkList['product-dashboard'] = 'By default, go to the latest product dashboard to see the current product overview'; $lang->my->productLinkList['product-browse'] = 'By default, go to the list of requirements for the most recent product and see the requirements under the current product'; +$lang->my->productLinkList['product-kanban'] = 'Enter the Product Kanban by default, and check the progress of all Products'; global $config; $lang->my->projectLinkList = array(); $lang->my->projectLinkList['project-browse'] = 'By default, you go to the project list, where you can view all the projects'; $lang->my->projectLinkList['project-execution'] = 'Go to Project-Exection by default. You can check all information in Execution'; $lang->my->projectLinkList['project-index'] = 'By default, go to the most recent project dashboard to see the current project overview'; +if($config->systemMode == 'new') $lang->my->projectLinkList['project-kanban'] = 'Enter the Project Kanban by default, and check the progress of all Projects'; $lang->my->executionLinkList = array(); if($config->systemMode == 'new') diff --git a/module/my/lang/zh-cn.php b/module/my/lang/zh-cn.php index 6fd0e3f26e..6eef0eb79e 100644 --- a/module/my/lang/zh-cn.php +++ b/module/my/lang/zh-cn.php @@ -93,18 +93,21 @@ if($config->systemMode == 'new') $lang->my->executionLink = '执行默认着陆 $lang->my->programLinkList = array(); $lang->my->programLinkList['program-browse'] = '默认进入项目集列表,可以查看所有的项目集'; $lang->my->programLinkList['program-project'] = '默认进入最近一个项目集的项目列表,可以查看当前项目集下所有项目'; +if($config->systemMode == 'new') $lang->my->programLinkList['program-kanban'] = '默认进入项目集看板,可以可视化的查看到所有项目集的进展情况'; $lang->my->productLinkList = array(); $lang->my->productLinkList['product-index'] = '默认进入产品主页,可以了解公司整体的产品状况'; $lang->my->productLinkList['product-all'] = '默认进入产品列表,可以查看所有的产品'; $lang->my->productLinkList['product-dashboard'] = '默认进入最近一个产品仪表盘,可以查看当前产品概况'; $lang->my->productLinkList['product-browse'] = '默认进入最近一个产品的需求列表,可以查看当前产品下的需求信息'; +$lang->my->productLinkList['product-kanban'] = '默认进入产品看板,可以可视化的查看到所有产品的进展情况'; global $config; $lang->my->projectLinkList = array(); $lang->my->projectLinkList['project-browse'] = '默认进入项目列表,可以查看所有的项目'; $lang->my->projectLinkList['project-execution'] = '默认进入项目下所有执行列表,查看所有执行信息'; $lang->my->projectLinkList['project-index'] = '默认进入最近一个项目仪表盘,可以查看当前项目概况'; +if($config->systemMode == 'new') $lang->my->projectLinkList['project-kanban'] = '默认进入项目看板,可以可视化的查看到所有项目的进展情况'; $lang->my->executionLinkList = array(); if($config->systemMode == 'new') diff --git a/module/my/lang/zh-tw.php b/module/my/lang/zh-tw.php index c0b822232a..2eb7d3cded 100644 --- a/module/my/lang/zh-tw.php +++ b/module/my/lang/zh-tw.php @@ -93,18 +93,21 @@ if($config->systemMode == 'new') $lang->my->executionLink = '執行預設着陸 $lang->my->programLinkList = array(); $lang->my->programLinkList['program-browse'] = '預設進入項目集列表,可以查看所有的項目集'; $lang->my->programLinkList['program-project'] = '預設進入最近一個項目集的項目列表,可以查看當前項目集下所有項目'; +if($config->systemMode == 'new') $lang->my->programLinkList['program-kanban'] = '默認進入項目集看板,可以視覺化的查看到所有項目集的進展情况'; $lang->my->productLinkList = array(); $lang->my->productLinkList['product-index'] = '預設進入產品主頁,可以瞭解公司整體的產品狀況'; $lang->my->productLinkList['product-all'] = '預設進入產品列表,可以查看所有的產品'; $lang->my->productLinkList['product-dashboard'] = '預設進入最近一個產品儀表盤,可以查看當前產品概況'; $lang->my->productLinkList['product-browse'] = '預設進入最近一個產品的需求列表,可以查看當前產品下的需求信息'; +$lang->my->productLinkList['product-kanban'] = '默認進入產品看板,可以視覺化的查看到所有產品的進展情况'; global $config; $lang->my->projectLinkList = array(); $lang->my->projectLinkList['project-browse'] = '預設進入項目列表,可以查看所有的項目'; $lang->my->projectLinkList['project-execution'] = '預設進入項目下所有執行列表,查看所有執行信息'; $lang->my->projectLinkList['project-index'] = '預設進入最近一個項目儀表盤,可以查看當前項目概況'; +if($config->systemMode == 'new') $lang->my->projectLinkList['project-kanban'] = '默認進入項目看板,可以視覺化的查看到所有項目的進展情况'; $lang->my->executionLinkList = array(); if($config->systemMode == 'new') From 85ad745929aef5ba20d3ebe53389b73ab3a8cfd8 Mon Sep 17 00:00:00 2001 From: hufangzhou Date: Mon, 6 Sep 2021 13:06:33 +0800 Subject: [PATCH 2/8] * Add the recall in project story list, add the review and recall in execution story list. --- module/execution/control.php | 3 +++ module/execution/view/story.html.php | 18 +++++++++++++++++- module/story/config.php | 2 +- module/story/model.php | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/module/execution/control.php b/module/execution/control.php index bdec90fbd6..b71cf5abfd 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -736,6 +736,9 @@ class execution extends control $stories = $this->story->getExecutionStories($executionID, 0, 0, $sort, $type, $param, 'story', '', $pager); $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story', false); + + if(!empty($stories)) $stories = $this->story->mergeReviewer($stories); + $users = $this->user->getPairs('noletter'); /* Build the search form. */ diff --git a/module/execution/view/story.html.php b/module/execution/view/story.html.php index 71362660f2..1a10276487 100644 --- a/module/execution/view/story.html.php +++ b/module/execution/view/story.html.php @@ -196,7 +196,7 @@ story->taskCountAB;?> story->bugCountAB;?> story->caseCountAB;?> - actions;?> + actions;?> @@ -256,6 +256,22 @@ { $param = "executionID={$execution->id}&story={$story->id}&moduleID={$story->module}"; + $story->reviewer = isset($story->reviewer) ? $story->reviewer : array(); + $story->notReview = isset($story->notReview) ? $story->notReview : array(); + + if(common::hasPriv('story', 'review')) + { + $reviewDisabled = in_array($app->user->account, $story->notReview) and ($story->status == 'draft' or $story->status == 'changed') ? '' : 'disabled'; + $story->from = 'execution'; + common::printIcon('story', 'review', "story={$story->id}&from=story", $story, 'list', 'search', '', $reviewDisabled, false, "data-group=execution"); + } + + if(common::hasPriv('story', 'recall')) + { + $recallDisabled = empty($story->reviewedBy) and strpos('draft,changed', $story->status) !== false and !empty($story->reviewer) ? '' : 'disabled'; + common::printIcon('story', 'recall', "story={$story->id}", $story, 'list', 'back', 'hiddenwin', $recallDisabled, '', '', $lang->story->recall); + } + $lang->task->create = $lang->execution->wbs; $toTaskDisabled = strpos('draft,closed', $story->status) !== false ? 'disabled' : ''; if(commonModel::isTutorialMode()) diff --git a/module/story/config.php b/module/story/config.php index 7fb95e824d..71fd167136 100644 --- a/module/story/config.php +++ b/module/story/config.php @@ -199,5 +199,5 @@ $config->story->datatable->fieldList['caseCount']['name'] = $lang->story->ca $config->story->datatable->fieldList['actions']['title'] = 'actions'; $config->story->datatable->fieldList['actions']['fixed'] = 'right'; -$config->story->datatable->fieldList['actions']['width'] = '200'; +$config->story->datatable->fieldList['actions']['width'] = $app->tab == 'project' ? '220' : '200'; $config->story->datatable->fieldList['actions']['required'] = 'yes'; diff --git a/module/story/model.php b/module/story/model.php index 0af26c0100..1b4e868659 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -3769,7 +3769,7 @@ class storyModel extends model common::printIcon('story', 'change', $vars . "&from=$story->from", $story, 'list', 'alter', '', '', false, "data-group=$story->from"); common::printIcon('story', 'review', $vars . "&from=$story->from", $story, 'list', 'search', '', '', false, "data-group=$story->from"); - if($this->app->tab != 'project') common::printIcon('story', 'recall', $vars, $story, 'list', 'back', 'hiddenwin', '', '', '', $this->lang->story->recall); + common::printIcon('story', 'recall', $vars, $story, 'list', 'back', 'hiddenwin', '', '', '', $this->lang->story->recall); common::printIcon('story', 'close', $vars, $story, 'list', '', '', 'iframe', true); common::printIcon('story', 'edit', $vars . "&from=$story->from", $story, 'list', '', '', '', false, "data-group=$story->from"); if($story->type != 'requirement') common::printIcon('story', 'createCase', "productID=$story->product&branch=$story->branch&module=0&from=¶m=0&$vars", $story, 'list', 'sitemap', '', '', false, "data-app='qa'"); From 61df601ab8dd6a9f0b5d54af09b662d44ac173b8 Mon Sep 17 00:00:00 2001 From: Hao Sun Date: Mon, 6 Sep 2021 17:10:46 +0800 Subject: [PATCH 3/8] * change back color for default user avatar. --- lib/front/front.class.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/front/front.class.php b/lib/front/front.class.php index 671e077dd2..5d0a014216 100644 --- a/lib/front/front.class.php +++ b/lib/front/front.class.php @@ -243,7 +243,7 @@ class html extends baseHTML * @access public * @return string */ - static public function avatar($user, $size = '', $className = 'avatar-circle', $attrib = '', $tag = 'div') + static public function avatar($user, $size = '', $className = 'avatar-circle', $attrib = '', $tag = 'div', $hueDistance = 43, $saturation = '40%', $lightness = '60%') { if(is_string($user)) { @@ -273,8 +273,8 @@ class html extends baseHTML if(!$hasImage) { - $colorHue = (html::stringToCode($user->account) * 43) % 360; - $style .= "background: hsl($colorHue, 100%, 58%);"; + $colorHue = (html::stringToCode($user->account) * $hueDistance) % 360; + $style .= "background: hsl($colorHue, $saturation, $lightness);"; } if(!empty($style)) $style = "style='$style'"; From 4cd16454364382f963f947c309ceaf0c1b2a2f06 Mon Sep 17 00:00:00 2001 From: Hao Sun Date: Mon, 6 Sep 2021 17:17:28 +0800 Subject: [PATCH 4/8] * task #42141, adjust modal style in page tutorial. --- module/tutorial/css/index.css | 20 ++++++++++---------- module/tutorial/view/index.html.php | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/module/tutorial/css/index.css b/module/tutorial/css/index.css index 0857f85e93..8b85af8af0 100644 --- a/module/tutorial/css/index.css +++ b/module/tutorial/css/index.css @@ -25,7 +25,7 @@ #task .task-desc > ul > li[data-target]:after {content: ' '; display: block; position: absolute; left: 0; top: -100%; width: 100%; height: 100%; border: 2px solid #F1A325; transition: top .5s; pointer-events: none; border-radius: 4px;} #task .task-desc > ul > li[data-target]:before {font-family: ZentaoIcon; content: '\e836'; color: #333;} #task .task-desc > ul > li[data-target].finish:after {top: 100%;} -#task .task-desc > ul > li[data-target].finish:before {content: '\e92f'; color: #00da88;} +#task .task-desc > ul > li[data-target].finish:before {content: '\e92f'; color: #43a047;} #task .task-desc > ul > li[data-target].active {background: #FFF0D5;} #task .task-desc > ul > li[data-target].active:after {top: 0%;} #task .task-desc > ul > li[data-target].active:before {font-family: ZentaoIcon; color: #EA644A; content: '\e92c';} @@ -35,27 +35,27 @@ #openTaskPage > div > .icon {display: inline-block; width: 30px; text-align: center;} #openTaskPage:hover {background: #EBF2F9;} #openTaskPage > .normal {top: 0;} -#openTaskPage.open {color: #00da88; background: #DDF4DF;} +#openTaskPage.open {color: #43a047; background: #DDF4DF;} #openTaskPage.open:hover {color: #006af1; background: #EBF2F9;} #openTaskPage > .opened, #openTaskPage > .reload, #openTaskPage.open > .reload {top: 36px;} #openTaskPage.open > .normal, #openTaskPage.open:hover > .opened {top: -36px;} #openTaskPage.open > .opened, #openTaskPage.open:hover > .reload {top: 0;} -#task.finish > .panel-heading {background: #00da88;} +#task.finish > .panel-heading {background: #43a047;} #task.finish > .panel-heading .task-name {text-decoration: line-through;} #task.finish.finish > .panel-heading > .pull-right {opacity: 1; transform: scale(1);} #task .task-nav {color: #006af1; display: inline-block; background: #EBF2F9; padding: 1px 5px 0; transition: all .2s;} #task .task-nav:hover {color: #fff; background: #006af1; cursor: pointer;} #tasksProgress {background: #ddd; position: relative;} #tasksProgress > .progress-bar {background: #006af1; height: 8px; border-radius: 4px; margin-bottom: 15px; transition: background .4s, width .4s;} -#tasksProgress.finish > .progress-bar {width: 100% !important; background: #00da88;} +#tasksProgress.finish > .progress-bar {width: 100% !important; background: #43a047;} #tasksProgress .progress-text {position: absolute; right: 0; top: -25px; font-weight: bold; opacity: 0.7; color: #006af1;} -#tasksProgress.finish .progress-text {color: #00da88; opacity: 1;} +#tasksProgress.finish .progress-text {color: #43a047; opacity: 1;} #tasks {background: #fff; box-shadow: 0 1px 1px rgba(0,0,0,.05), 0 2px 6px 0 rgba(0,0,0,0.045); border-radius: 6px;} #tasks > li > a {padding: 8px 8px 8px 12px;} -#tasks > li > a > .pull-right {filter: alpha(opacity=0); opacity: 0; transition: opacity .4s, transform .4s; transform: scale(2.5, 2); color: #00da88;} -#tasks > li.finish > a, #tasks > li.finish.active > a {box-shadow: inset 4px 0 0 #00da88; border-left-color: #00da88; color: #808080;} +#tasks > li > a > .pull-right {filter: alpha(opacity=0); opacity: 0; transition: opacity .4s, transform .4s; transform: scale(2.5, 2); color: #43a047;} +#tasks > li.finish > a, #tasks > li.finish.active > a {box-shadow: inset 4px 0 0 #43a047; border-left-color: #43a047; color: #808080;} #tasks > li.finish > a > .pull-right {filter: alpha(opacity=100); opacity: 1; transform: scale(1);} #tasks > li.finish > a > .task-name {text-decoration: line-through;} #tasks > li.active > a, #tasks > li.active.finish > a {box-shadow: inset 4px 0 0 #006af1; border-color: #ddd; border-left-color: #006af1; color: #006af1; background: #EBF2F9;} @@ -63,16 +63,16 @@ #taskModalBack {position: absolute; top: 0; left: 0; bottom: 0; right: 0; background: #fff; transition: opacity .4s; opacity: 0; display: none;} #taskModalBack.in {filter: alpha(opacity=60); opacity: 0.6;} -#taskModal {width: 500px; position: absolute; background: #00da88; color: #fff; text-align: center; overflow: hidden; transform: scale(.5) translate(0, 200px) rotateX(120deg); transitizon: transform .4s, opacity .4s; opacity: 0; transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); top: 0; padding: 30px; left: 50%; margin-left: -250px; top: 20%; box-shadow: 0 0 20px 0 rgba(0,0,0,.25); display: none;} +#taskModal {width: 500px; position: absolute; background: #43a047; color: #fff; text-align: center; overflow: hidden; transform: scale(.5) translate(0, 200px) rotateX(120deg); transitizon: transform .4s, opacity .4s; opacity: 0; transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); top: 0; padding: 30px; left: 50%; margin-left: -250px; top: 20%; box-shadow: 0 0 20px 0 rgba(0,0,0,.25); display: none;} #taskModal.in {transform: scale(1) translate(0) rotateX(0); opacity: 1;} #taskModal h3 {margin-bottom: 20px;} #taskModal .start-icon > .icon-spin {-moz-animation: spin 10s infinite linear; -o-animation: spin 10s infinite linear; -webkit-animation: spin 10s infinite linear; animation: spin 10s infinite linear;} #taskModal .start-icon {width: 60px; height: 60px; text-align: center; margin: 10px auto; position: relative;} #taskModal .start-icon > .icon-front {font-size: 56px; color: #fff; line-height: 60px; width: 60px; height: 60px; position: absolute; left: 0; top: 0;} -#taskModal .btn-success {border-color: #DDF4DF;} -#taskModal .btn-success:hover {background: #00c178;} #taskModal > .close {position: absolute; right: 15px; top: 10px;} #taskModal > .finish-all {display: none;} #taskModal > .finish {display: block;} #taskModal.show-all > .finish-all {display: block;} #taskModal.show-all > .finish {display: none;} +#taskModal .btn-outline {background-color: transparent; border: 1px solid #fff; color: #fff} +#taskModal .btn-outline:hover {background-color: #fff; color: #43a047} diff --git a/module/tutorial/view/index.html.php b/module/tutorial/view/index.html.php index 3d834d5e8b..0710bcd8ea 100644 --- a/module/tutorial/view/index.html.php +++ b/module/tutorial/view/index.html.php @@ -21,12 +21,12 @@

tutorial->congratulation;?>

-   ' class='btn btn-success'> tutorial->exit;?> +   ' class='btn btn-outline'> tutorial->exit;?>

tutorial->congratulateTask;?>

- +
From 1a6a47c8445c80b148c3288102d08fd692b67eea Mon Sep 17 00:00:00 2001 From: Hao Sun Date: Mon, 6 Sep 2021 17:19:24 +0800 Subject: [PATCH 5/8] * typo. --- module/tutorial/css/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/tutorial/css/index.css b/module/tutorial/css/index.css index 8b85af8af0..fd77de5e6d 100644 --- a/module/tutorial/css/index.css +++ b/module/tutorial/css/index.css @@ -63,7 +63,7 @@ #taskModalBack {position: absolute; top: 0; left: 0; bottom: 0; right: 0; background: #fff; transition: opacity .4s; opacity: 0; display: none;} #taskModalBack.in {filter: alpha(opacity=60); opacity: 0.6;} -#taskModal {width: 500px; position: absolute; background: #43a047; color: #fff; text-align: center; overflow: hidden; transform: scale(.5) translate(0, 200px) rotateX(120deg); transitizon: transform .4s, opacity .4s; opacity: 0; transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); top: 0; padding: 30px; left: 50%; margin-left: -250px; top: 20%; box-shadow: 0 0 20px 0 rgba(0,0,0,.25); display: none;} +#taskModal {width: 500px; position: absolute; background: #43a047; color: #fff; text-align: center; overflow: hidden; transform: scale(.5) translate(0, 200px) rotateX(120deg); transition: transform .4s, opacity .4s; opacity: 0; transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); top: 0; padding: 30px; left: 50%; margin-left: -250px; top: 20%; box-shadow: 0 0 20px 0 rgba(0,0,0,.25); display: none;} #taskModal.in {transform: scale(1) translate(0) rotateX(0); opacity: 1;} #taskModal h3 {margin-bottom: 20px;} #taskModal .start-icon > .icon-spin {-moz-animation: spin 10s infinite linear; -o-animation: spin 10s infinite linear; -webkit-animation: spin 10s infinite linear; animation: spin 10s infinite linear;} From 968e115af3855c441f1cf783dd41082aa59af5e2 Mon Sep 17 00:00:00 2001 From: zenggang Date: Mon, 6 Sep 2021 17:50:28 +0800 Subject: [PATCH 6/8] * Fix bug#14935 --- module/program/js/common.js | 5 ----- module/program/js/edit.js | 4 ++++ module/program/view/edit.html.php | 5 ++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/module/program/js/common.js b/module/program/js/common.js index b12b02cfa1..f34e41c406 100644 --- a/module/program/js/common.js +++ b/module/program/js/common.js @@ -104,13 +104,8 @@ function computeEndDate(delta) if(delta == 999) { $('#end').val(longTime); - $('#end').attr('disabled', 'disabled'); return false; } - else - { - $('#end').removeAttr('disabled'); - } beginDate = convertStringToDate(beginDate); if((delta == 7 || delta == 14) && (beginDate.getDay() == 1)) diff --git a/module/program/js/edit.js b/module/program/js/edit.js index d69473ebad..df353f8db3 100644 --- a/module/program/js/edit.js +++ b/module/program/js/edit.js @@ -78,4 +78,8 @@ $(function() $('#exchangeRate').val(exchangeRate); $('#changeUnitTip').modal('hide'); }) + + /* If end is longtime, set the default date to today */ + var today = $.zui.formatDate(new Date(), 'yyyy-MM-dd'); + if($('#end').val() == longTime) $('#end').val(today).datetimepicker('update').val(longTime); }); diff --git a/module/program/view/edit.html.php b/module/program/view/edit.html.php index 810baaa9fd..06fe71d320 100644 --- a/module/program/view/edit.html.php +++ b/module/program/view/edit.html.php @@ -75,9 +75,8 @@ begin, "class='form-control form-date' placeholder='" . $lang->project->begin . "' required");?> project->to;?> end == LONG_TIME ? 'disabled' : ''; - $end = $program->end == LONG_TIME ? $lang->program->longTime : $program->end; - echo html::input('end', $end, "class='form-control form-date' $disabledEnd placeholder='" . $lang->project->end . "' required"); + $end = $program->end == LONG_TIME ? $lang->program->longTime : $program->end; + echo html::input('end', $end, "class='form-control form-date' placeholder='" . $lang->project->end . "' required"); ?> From 75ac012b6cadf2a5183ac556236f6e97ae3d6c56 Mon Sep 17 00:00:00 2001 From: Hao Sun Date: Tue, 7 Sep 2021 08:26:34 +0800 Subject: [PATCH 7/8] + add common kanban component. --- module/common/view/kanban.html.php | 305 +++++++++++++++++++++++++++++ www/js/zui/kanban/min.css | 6 + www/js/zui/kanban/min.js | 7 + 3 files changed, 318 insertions(+) create mode 100644 module/common/view/kanban.html.php create mode 100644 www/js/zui/kanban/min.css create mode 100644 www/js/zui/kanban/min.js diff --git a/module/common/view/kanban.html.php b/module/common/view/kanban.html.php new file mode 100644 index 0000000000..136b91145d --- /dev/null +++ b/module/common/view/kanban.html.php @@ -0,0 +1,305 @@ +getExtViewFile(__FILE__)){include $extView; return helper::cd();}?> + + + + diff --git a/www/js/zui/kanban/min.css b/www/js/zui/kanban/min.css new file mode 100644 index 0000000000..ff03a83e1c --- /dev/null +++ b/www/js/zui/kanban/min.css @@ -0,0 +1,6 @@ +/*! + * ZUI: ZUI Kanban View - v1.9.2 - 2021-09-06 + * http://openzui.com + * GitHub: https://github.com/easysoft/zui.git + * Copyright (c) 2021 cnezsoft.com; Licensed MIT + */.kanban{min-height:300px;overflow-x:auto}.kanban-header,.kanban-lane,.kanban-sub-lane{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;flex-direction:row;background-color:#f1f3f5;border-bottom:1px solid #d5d9de;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap}.no-flex .kanban-lane{padding-left:20px}.kanban-header{padding-left:20px;background-color:rgba(0,0,0,.07)}.kanban-col{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.not-use-flex .kanban-col{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none}.no-flex .kanban-col{float:left}.kanban-col+.kanban-col{border-left:1px solid #d5d9de}.kanban-header-col{position:relative;height:32px}.kanban-header-col>.title{display:block;margin:6px 32px;line-height:20px;text-align:center}.kanban-header-col>.title>.text{display:inline-block;max-width:100px;margin:0 5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kanban-header-col>.title>.count,.kanban-header-col>.title>.icon{position:relative;top:-6px}.kanban-header-col[data-id=ADD]>.title{margin:6px;opacity:.7}.kanban-header-col[data-id=ADD]>.actions{display:none}.kanban-header-col>.actions{position:absolute;top:0;right:0}.kanban-header-col>.actions>.btn{min-width:32px}.kanban-header-col>.actions>.btn>.icon{opacity:.5}.kanban-lane{min-height:100px;margin-bottom:3px}.kanban-lane-name{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:20px;padding:10px 0;color:#fff;text-align:center;background-color:#3dc6fd;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none}.no-flex .kanban-lane-name{position:absolute;top:0;bottom:0;left:0}.kanban-header+.kanban-lane>.kanban-lane-name{margin-top:-33px}.kanban-lane-name .char{display:block;line-height:1;white-space:pre}.kanban-lane-name .char.rotate-90{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.kanban-lane-name.is-arab{font-size:12px;-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.kanban-lane-name.is-arab .char{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.kanban-sub-lanes{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;flex-direction:column;-webkit-box-flex:1;-webkit-flex:auto;-ms-flex:auto;flex:auto;-webkit-box-align:stretch;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-pack:stretch;-webkit-justify-content:stretch;-ms-flex-pack:stretch;justify-content:stretch;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column}.kanban-sub-lane{-webkit-box-flex:1;-webkit-flex:auto;-ms-flex:auto;flex:auto}.kanban-lane-col{max-height:300px;overflow:auto}.kanban-lane-col.drop-to{outline:3px solid rgba(255,190,57,.25);outline-offset:-3px}.kanban-lane-col.drop-to .kanban-lane-actions>.btn{background-color:rgba(255,190,57,.25);border:1px dotted #ff9800}.kanban-lane-col.drop-to .kanban-lane-actions>.btn>span{opacity:0}.kanban-lane-col[data-type=EMPTY]{background-color:#fff}.kanban-lane-items{padding:0 15px}.kanban-lane-actions{padding:10px 15px}.kanban-item{padding:10px;margin-top:10px;background:#fff;border:1px solid #fff;border-radius:4px;-webkit-transition:-webkit-box-shadow .2s,-webkit-transform .2s;-o-transition:box-shadow .2s,-o-transform .2s;transition:-webkit-box-shadow .2s,-webkit-transform .2s;transition:box-shadow .2s,transform .2s;transition:box-shadow .2s,transform .2s,-webkit-box-shadow .2s,-webkit-transform .2s,-o-transform .2s}.kanban-item.drag-from{background-color:#aaa;opacity:.2}.kanban-item:hover{border-color:rgba(0,0,0,.1);-webkit-box-shadow:0 4px 10px 0 rgba(0,0,0,.09);box-shadow:0 4px 10px 0 rgba(0,0,0,.09)}.kanban-item.drag-shadow{border-color:rgba(0,0,0,.2);-webkit-box-shadow:0 4px 10px 0 rgba(0,0,0,.09),0 4px 20px 0 rgba(0,0,0,.5);box-shadow:0 4px 10px 0 rgba(0,0,0,.09),0 4px 20px 0 rgba(0,0,0,.5);-webkit-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)} diff --git a/www/js/zui/kanban/min.js b/www/js/zui/kanban/min.js new file mode 100644 index 0000000000..3db4c2248f --- /dev/null +++ b/www/js/zui/kanban/min.js @@ -0,0 +1,7 @@ +/*! + * ZUI: ZUI Kanban View - v1.9.2 - 2021-09-06 + * http://openzui.com + * GitHub: https://github.com/easysoft/zui.git + * Copyright (c) 2021 cnezsoft.com; Licensed MIT + */ +!function(a){"use strict";var n="zui.kanban",e="object"==typeof CSS&&CSS.supports("display","flex"),t=function(d,i){var o=this;o.name=n,o.$=a(d),o.options=i=a.extend({},t.DEFAULTS,this.$.data(),i),o.data=i.data||[],o.render(o.data);var r=function(n){if(i.onAction){var e=a(this);i.onAction(e.data("action"),e,n,o)}};if(o.$.on("click",".action",r).on("dblclick",".action-dbc",r),"auto"===i.droppable&&(i.droppable=!i.readonly),i.droppable){var s={selector:".kanban-item",target:'.kanban-lane-col:not([data-type="EMPTY"])',drop:function(a){"function"==typeof i.droppable?i.droppable(a):i.onAction&&i.onAction("dropItem",a.element,a,o)}};"object"==typeof i.droppable&&a.extend(s,i.droppable),o.$.droppable(s)}e&&i.useFlex||a(window).on("resize",function(){o.adjustSize()}),!i.useFlex&&e&&o.$.addClass("not-use-flex")};t.prototype.render=function(a){var n=this;a&&(n.data=a),n.data&&!Array.isArray(n.data)&&(n.data=[n.data]);var e=n.data||[];n.$.toggleClass("kanban-readonly",n.options.readonly),n.$.children(".kanban-board").addClass("kanban-expired");for(var t=0;t-1){var d=this.data[t];n=a.extend(d,n),this.data[t]=n}else this.data.push(n)}n.id||(n.id=a.zui.uuid());var i=this,o=i.$,r=o.children('.kanban-board[data-id="'+n.id+'"]');r.length?r.removeClass("kanban-expired"):(r=a('
').appendTo(o),e||r.addClass("no-flex")),i.renderKanbanHeader(n,r),r.children(".kanban-lane").addClass("kanban-expired");for(var s=n.lanes||[],l=0;l .kanban-lane-items > .kanban-item').length;r.find('.kanban-header-col[data-id="'+c+'"] > .title > .count').text(p||"")}i.adjustKanbanSize(n,r),i.options.onRenderKanban&&i.options.onRenderKanban(r,n,i)},t.prototype.renderKanbanHeader=function(n,t){var d=this;t=t||d.$.children('.kanban-board[data-id="'+n.id+'"]');var i=t.children(".kanban-header");i.length||(i=a('
').prependTo(t),e||i.addClass("clearfix")),i.children(".kanban-col").addClass("kanban-expired");for(var o=n.columns,r=0;r',"<"+(d?"a":"div")+' class="title">','','','',"",""].join("")).appendTo(e),d?t.children(".title").addClass("action").attr("data-action","addCol"):(t.children(".title").addClass("action-dbc").attr("data-action","editCol"),this.options.readonly||t.append(['
','',"
"].join("")));var i=this.options.minColumnWidth;i&&t.css("min-width",i)}t.data("col",n),t.attr("data-type",n.type),t.find(".title>.icon").attr("class","icon icon-"+(n.icon||"")),t.find(".title>.text").text(n.name),t.find(".title>.count").text(n.count||""),this.options.onRenderHeaderCol&&this.options.onRenderHeaderCol(t,n,e)},t.prototype.renderLane=function(n,t,d){var i=this;d=d||i.$.children('.kanban-board[data-id="'+n.kanban+'"]');var o=d.children('.kanban-lane[data-id="'+n.id+'"]');o.length?o.removeClass("kanban-expired"):(o=a('
').appendTo(d),e||o.addClass("clearfix")),o.data("lane",n);var r=o.children('.kanban-lane-name[data-id="'+n.id+'"]');r.length||(r=a('
').appendTo(o));for(var s=[],l=0;l15){s.push('…');break}s.push(''+n.name[l]+"")}r.toggleClass("is-arab",/^[\da-zA-Z ]+$/.test(n.name)).html(''+s.join("")+""),o.children(".kanban-col,.kanban-sub-lanes").addClass("kanban-expired"),n.subLanes?i.renderSubLanes(n,t,o):i.renderLaneItems(t,n.items,o),i.options.readonly||i.renderLaneCol({id:"EMPTY",type:"EMPTY"},o),o.children(".kanban-expired").remove()},t.prototype.renderSubLanes=function(n,e,t){var d=this,i=t.children('.kanban-sub-lanes[data-id="'+n.id+'"]');i.length?i.removeClass("kanban-expired"):i=a('
').appendTo(t),i.children(".kanban-sub-lane").addClass("kanban-expired");for(var o=0;o').appendTo(d),e||i.addClass("clearfix")),i.children(".kanban-col").addClass("kanban-expired"),this.renderLaneItems(t,n.items,i),i.children(".kanban-expired").remove()},t.prototype.renderLaneItems=function(a,n,e){for(var t=this,d=0;d').appendTo(e),"EMPTY"!==n.id&&(t.append('
'),this.options.readonly||t.append(['
','","
"].join("")));var d=this.options.maxColHeight,i=this.options.minColumnWidth;d&&t.css("max-height",d),i&&t.css("min-width",i),this.options.laneColClass&&t.addClass(this.options.laneColClass)}return"EMPTY"===n.id&&t.appendTo(e),t.attr("data-type",n.type),t},t.prototype.renderLaneItem=function(n,e,t){var d=e.children('.kanban-item[data-id="'+n.id+'"]');if(d.length?d.removeClass("kanban-expired"):d=a('
').appendTo(e),this.options.itemRender)this.options.itemRender(n,d,t);else{var i=d.find(".title");i.length||(i=a('
').appendTo(d)),i.text(n.name||n.title)}return d},t.prototype.adjustKanbanSize=function(n,t){var d=this,i=d.options.laneNameWidth,o=n.columns.length+(d.options.readonly?0:1),r=d.options.minColumnWidth*o+i;if(t=t||d.$.children('.kanban-board[data-id="'+n.id+'"]'),t.css("min-width",r),!d.options.useFlex||!e){var s=Math.max(r,d.$.width()),l=Math.floor((s-i)/o);t.find(".kanban-col").css("width",l),t.children(".kanban-lane").each(function(){var n=a(this),e=n.height();n.children(".kanban-col").css("height",e)})}},t.prototype.adjustSize=function(){for(var a=0;a Date: Tue, 7 Sep 2021 08:26:58 +0800 Subject: [PATCH 8/8] * implement kanban pages with kanban component. --- module/product/control.php | 20 +-- module/product/css/kanban.css | 30 ---- module/product/js/kanban.js | 108 +++++++++++ module/product/view/kanban.html.php | 268 ++++++---------------------- module/program/js/kanban.js | 109 +++++++++++ module/program/view/kanban.html.php | 208 ++++----------------- module/project/js/kanban.js | 85 ++++++--- module/project/view/kanban.html.php | 166 ++++------------- 8 files changed, 412 insertions(+), 582 deletions(-) create mode 100644 module/product/js/kanban.js create mode 100644 module/program/js/kanban.js diff --git a/module/product/control.php b/module/product/control.php index 58bdeeace2..ac802686b3 100644 --- a/module/product/control.php +++ b/module/product/control.php @@ -1159,31 +1159,25 @@ class product extends control $kanbanGroup = $this->product->getStats4Kanban(); extract($kanbanGroup); - $products = array(); $myProducts = array(); $otherProducts = array(); foreach($productList as $productID => $product) { - if($product->status == 'normal' and $product->PO == $this->app->user->account) - { - $myProducts[$product->program][$productID] = $productID; - } - elseif($product->status == 'normal' and !($product->PO == $this->app->user->account)) - { - $otherProducts[$product->program][$productID] = $productID; - } + if($product->status != 'normal') continue; + if($product->PO == $this->app->user->account) $myProducts[$product->program][] = $productID; + else $otherProducts[$product->program][] = $productID; } - $products['myProducts'] = $myProducts; - $products['otherProducts'] = $otherProducts; + $kanbanList = array(); + if(!empty($myProducts)) $kanbanList['my'] = $myProducts; + if(!empty($otherProducts)) $kanbanList['other'] = $otherProducts; $this->view->title = $this->lang->product->kanban; - $this->view->products = $products; + $this->view->kanbanList = $kanbanList; $this->view->programList = array(0 => $this->lang->product->emptyProgram) + $programList; $this->view->productList = $productList; $this->view->planList = $planList; $this->view->projectList = $projectList; - $this->view->executionList = $executionList; $this->view->projectProduct = $projectProduct; $this->view->latestExecutions = $projectLatestExecutions; $this->view->hourList = $hourList; diff --git a/module/product/css/kanban.css b/module/product/css/kanban.css index eba3633328..e69de29bb2 100644 --- a/module/product/css/kanban.css +++ b/module/product/css/kanban.css @@ -1,30 +0,0 @@ -#kanban {padding: 10px;} -.table caption {background: #fff; border-top-left-radius: 2px; border-top-right-radius: 2px; margin-bottom: 0; font-weight: 500; padding-top: 0;} -.table th {border: 2px solid #fff;} -.table td {border-top: 5px solid #fff; border-bottom: 5px solid #fff; border-left: 2px solid #fff; border-right: 2px solid #fff;} -.table tbody tr:last-child td {border-bottom: unset;} -td.plan, td.release, td.project, td.execution, td.classicProject {vertical-align: top !important;} -td.program {padding-top: 10px !important; padding-bottom: 10px !important;} -.main-table tbody>tr>td:first-child, .main-table thead>tr>th:first-child {padding: 0 4px 10px 4px;} - -.main-table tbody>tr>td {padding: 0 10px 10px;} -.table .program {writing-mode: vertical-lr; padding-left: 4px !important; color: #fff;} -td.product {padding-top: 10px !important;} -td.project, td.execution {padding: 0 0 10px 0 !important;} -td > div:not(.board) > .board-item:first-child {margin-top: 10px;} -td > div.board:first-child {margin-top: 5px;} -.fix-table-copy-wrapper thead > tr > th:first-child {background: none!important;} - -.board-item {border: 1px solid #ebebeb; padding: 3px 10px; cursor: default; border-radius: 2px; background-color: #fff; text-align: left;} -.board-item:hover {border-color: #ccc;} -.board {padding: 0 10px;} -.board + .board {margin-top: 5px;} -.board-item + .board-item {margin-top: 10px;} -.project .board, .execution .board {padding-top: 5px; border-top: 2px solid #fff;} -.project .board:first-child, .execution .board:first-child {padding-top: 5px; border-top: unset;} -.release .board-item {display: flex; flex-flow: row nowrap; justify-content: flex-start; align-items: center;} -.emptyBoard {padding: 3px 10px; cursor: default;} - -.project .table-row > .table-col:last-child, .execution .table-row > .table-col:last-child, .classicProject .table-row > .table-col:last-child{width: 24px; padding-top: 2px;} - -.scroll {overflow-x: hidden; overflow-y: auto;} diff --git a/module/product/js/kanban.js b/module/product/js/kanban.js new file mode 100644 index 0000000000..5a2a2b5b40 --- /dev/null +++ b/module/product/js/kanban.js @@ -0,0 +1,108 @@ +/** + * Process kanban data + * @param {string} key Kanban key, used as kanban id + * @param {Object} programsData Programs data + * @returns {Object} kanban data + */ +function processKanbanData(key, programsData) +{ + var kanbanId = key; + + /* Generate columns */ + var columns = []; + $.each(kanbanColumns, function(_, column) + { + columns.push($.extend({}, column, + { + kanban: kanbanId, + id: kanbanId + '-' + column.type, + })); + }); + + /* Format lanes data */ + var lanes = []; + $.each(programsData, function(programId, programProducts) + { + var programName = programList[programId]; + var subLanes = []; + programProducts.forEach(function(productID) + { + var product = productList[productID]; + var items = {}; + + /* unclosed products */ + var productItem = {id: 'product-' + productID, _id: productID, name: product.name}; + items.unclosedProduct = [productItem]; + + /* plans */ + items.unexpiredPlan = []; + var plans = planList[productID]; + if(plans) + { + $.each(plans, function(planID, plan) + { + items.unexpiredPlan.push($.extend({}, plan, {id: 'plan-' + planID, _id: planID})); + }); + } + + /* doing projects */ + if(kanbanColumns.doingProject) + { + items.doingProject = []; + var productProjects = projectProduct[productID]; + if(productProjects) + { + $.each(productProjects, function(projectID) + { + var project = projectList[projectID]; + if(!project) return; + var projectItem = $.extend({}, project, {id: 'project-' + projectID, _id: projectID}); + items.doingProject.push(projectItem); + }); + } + } + + /* doing execution */ + items.doingExecution = []; + var productProjects = projectProduct[productID]; + if(productProjects) + { + $.each(productProjects, function(projectID) + { + var execution = latestExecutions[projectID]; + if(!execution) return; + var executionItem = $.extend({}, execution, {id: 'execution-' + execution.id, _id: execution.id}); + items.doingExecution.push(executionItem); + }); + } + + /* normal release */ + items.normalRelease = []; + var releases = releaseList[productID]; + if(releases) + { + $.each(releases, function(releaseID, release) + { + if(!release) return; + var releaseItem = $.extend({}, release, {id: 'release-' + releaseID, _id: releaseID}); + items.normalRelease.push(releaseItem); + }); + } + + subLanes.push({id: kanbanId + '-' + programId + '-' + productID, items: items}); + }); + + lanes.push({id: programId, kanban: kanbanId, name: programName, subLanes: subLanes}); + }); + + return {id: kanbanId, columns: columns, lanes: lanes}; +} + +$(function() +{ + /* Init all kanbans */ + $.each(kanbanList, function(key, programsData) + { + $('#kanban-' + key).kanban({data: processKanbanData(key, programsData)}); + }); +}); diff --git a/module/product/view/kanban.html.php b/module/product/view/kanban.html.php index 7170d30f43..eb4591bc68 100644 --- a/module/product/view/kanban.html.php +++ b/module/product/view/kanban.html.php @@ -2,228 +2,64 @@ /** * The html product kanban file of kanban method of product module of ZenTaoPMS. * - * @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com) + * @copyright Copyright 2021-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com) * @license ZPL (http://zpl.pub/page/zplv12.html) - * @author Fangzhou Hu + * @author Hao Sun * @package ZenTaoPMS * @version $Id */ ?> -
- -
-

noData;?>

-
- - $programs):?> - -
- - - - - systemMode == 'new' ? "rowspan='2'" : ''; - $colspan = $config->systemMode == 'new' ? "colspan='2'" : ''; - ?> - systemMode == 'new'):?> - - - - - - - - systemMode == 'new'):?> - - - - - - - - - $programProduct):?> - - - 5 or $releaseCount > 5)) $scroll = 'scroll'; - ?> - - systemMode == 'new'):?> - - - - - systemMode == 'new'):?> - - - - systemMode == 'classic'):?> - - - - - - - - - -
product->myProduct : $lang->product->otherProduct;?>
class="w-20px" style="border-right: 0; border-left: 0; background: #32C5FF;"> style="border-left: 0px;">product->unclosedProduct;?>>product->unexpiredPlan;?>>product->doing;?>>product->normalRelease;?>
product->doingProject;?>product->doingExecution;?>
- - " . html::a($this->createLink('product', 'browse', "productID=$productID"), $productList[$productID]->name, '', "title={$productList[$productID]->name}") . '';?> - - name}>{$productList[$productID]->name}";?> - - -
- - $plan):?> -
- - createLink('productplan', 'view', "planID=$planID"), $plan->title, '', "title={$plan->title}");?> - - title}>{$plan->title}"?> - -
- - -
-
- - $project):?> - -
- delay) ? 'border-left: 3px solid red' : 'border-left: 3px solid #0bd986';?> -
-
-
- - createLink('project', 'index', "projectID=$projectID"), $projectList[$projectID]->name, '', "title={$projectList[$projectID]->name}");?> - - name}>{$projectList[$projectID]->name}"?> - -
-
-
-
-
hours->progress);?>
-
-
-
-
-
-
- - -
- - $project):?> -
- end); - $borderStyle = $delay > 0 ? 'border-left: 3px solid red' : 'border-left: 3px solid #0bd986'; - } - ?> - - id : 0;?> - name : ''?> -
-
-
- - - createLink('execution', 'task', "executionID=$executionID"), $executionName, '', "title={$executionName}");?> - - $executionName";?> - - -
-
- -
- -
-
progress);?>
-
-
- -
-
-
-
- - -
-
- - $execution):?> - end); - $borderStyle = $delay > 0 ? 'border-left: 3px solid red' : 'border-left: 3px solid #0bd986'; - ?> -
-
-
- - createLink('execution', 'task', "executionID=$executionID"), $execution->name, '', "title={$execution->name} class='text-ellipsis'");?> - - name}>{$execution->name}"?> - -
-
-
- -
-
progress);?>
-
-
-
-
-
- - -
-
-
- - $release):?> -
- - createLink('release', 'view', "releaseID=$releaseID"), $release->name, '', "title={$release->name} class='text-ellipsis'");?> - - name}>{$release->name}"?> - - marker) echo " ";?> -
- - -
-
-
- - + + +
+

noData;?>

- + $kanbanColumns['doingProject'] = array('name' => $lang->product->doingProject, 'type' => 'doingProject'); + $kanbanColumns['doingExecution'] = array('name' => $lang->product->doingExecution, 'type' => 'doingExecution'); +} +else +{ + $kanbanColumns['doingExecution'] = array('name' => $lang->product->doing, 'type' => 'doingExecution'); +} +$kanbanColumns['normalRelease'] = array('name' => $lang->product->normalRelease, 'type' => 'normalRelease'); +$userPrivs = array(); +$userPrivs['product'] = common::hasPriv('product', 'browse'); +$userPrivs['productplan'] = common::hasPriv('productplan', 'view'); +$userPrivs['project'] = common::hasPriv('project', 'index'); +$userPrivs['execution'] = common::hasPriv('execution', 'task'); +$userPrivs['release'] = common::hasPriv('release', 'view'); +js::set('kanbanColumns', $kanbanColumns); +js::set('userPrivs', $userPrivs); +js::set('kanbanList', $kanbanList); +js::set('programList', $programList); +js::set('productList', $productList); +js::set('planList', $planList); +js::set('projectList', $projectList); +js::set('projectProduct', $projectProduct); +js::set('latestExecutions', $latestExecutions); +js::set('releaseList', $releaseList); +js::set('hourList', $hourList); +js::set('doingText', $lang->product->doing); +?> + diff --git a/module/program/js/kanban.js b/module/program/js/kanban.js new file mode 100644 index 0000000000..7429511136 --- /dev/null +++ b/module/program/js/kanban.js @@ -0,0 +1,109 @@ +/** + * Process kanban data + * @param {string} key Kanban key, used as kanban id + * @param {Object} programsData Programs data + * @returns {Object} kanban data + */ +function processKanbanData(key, programsData) +{ + var kanbanId = key; + + /* Generate columns */ + var columns = []; + $.each(kanbanColumns, function(_, column) + { + columns.push($.extend({}, column, + { + kanban: kanbanId, + id: kanbanId + '-' + column.type, + })); + }); + + /* Format lanes data */ + var lanes = []; + $.each(programsData, function(programId, program) + { + var subLanes = []; + + $.each(program.products, function(productID, product) + { + var items = {}; + + /* unclosed products */ + var productItem = {id: 'product-' + productID, _id: productID, name: product.name}; + items.unclosedProduct = [productItem]; + + /* plans */ + items.unexpiredPlan = []; + var plans = product.plans; + if(plans) + { + $.each(plans, function(planID, plan) + { + items.unexpiredPlan.push($.extend({}, plan, {id: 'plan-' + planID, _id: planID})); + }); + } + + /* wait projects */ + items.waitProject = []; + var waitProjects = product.projects && product.projects.wait; + if(waitProjects) + { + $.each(waitProjects, function(projectID, project) + { + var projectItem = $.extend({}, project, {id: 'project-' + projectID, _id: projectID}); + items.waitProject.push(projectItem); + }); + } + + /* doing projects and executions */ + items.doingProject = []; + items.doingExecution = []; + var doingProjects = product.projects && product.projects.doing; + if(doingProjects) + { + $.each(doingProjects, function(projectID, project) + { + var projectItem = $.extend({}, project, {id: 'project-' + projectID, _id: projectID}); + items.doingProject.push(projectItem); + + var execution = project.execution; + if(!execution) return; + var executionItem = $.extend({}, execution, {id: 'execution-' + execution.id, _id: execution.id}); + items.doingExecution.push(executionItem); + }); + } + + /* normal release */ + items.normalRelease = []; + var releases = product.releases; + if(releases) + { + $.each(releases, function(releaseID, release) + { + if(!release) return; + var releaseItem = $.extend({}, release, {id: 'release-' + releaseID, _id: releaseID}); + items.normalRelease.push(releaseItem); + }); + } + + subLanes.push({id: kanbanId + '-' + programId + '-' + productID, items: items}); + }); + + var programItem = $.extend({}, program, {id: programId, kanban: kanbanId, subLanes: subLanes}); + lanes.push(programItem); + }); + + return {id: kanbanId, columns: columns, lanes: lanes}; +} + +$(function() +{ + /* Init all kanbans */ + $.each(kanbanGroup, function(key, programsData) + { + var $kanban = $('#kanban-' + key); + if(!$kanban.length) return; + $kanban.kanban({data: processKanbanData(key, programsData)}); + }); +}); diff --git a/module/program/view/kanban.html.php b/module/program/view/kanban.html.php index f3cb7c1fef..0421171509 100644 --- a/module/program/view/kanban.html.php +++ b/module/program/view/kanban.html.php @@ -1,182 +1,52 @@ + * @author Hao Sun * @package ZenTaoPMS * @version $Id */ ?> -
- $programGroup):?> - - -
-
-
program->kanban->typeList[$type];?>
-
- - - - - - - - - - - - - - - - - $program):?> - - - - products)):?> - products as $productID => $product):?> - projects['doing']) ? count($product->projects['doing']) : 0; - $planCounts = isset($product->plans) ? count($product->plans) : 0; - $releaseCount = isset($product->releases) ? count($product->releases) : 0; - if(($doingCounts < $planCounts || $doingCounts < $releaseCount) and ($planCounts > 5 or $releaseCount > 5)) $scroll = 'scroll'; - ?> - ';?> - - - - projects['doing'])):?> - - projects['doing'] as $projectID => $project):?> - ";?> - - - - - - ";?> - - - - - - - - ';?> - - - - - - - - - - - - - 9) $colorIndex = 0;?> - - -
program->kanban->openProducts;?>program->kanban->unexpiredPlans;?>program->kanban->waitingProjects;?>program->statusList['doing'];?>program->kanban->normalReleases;?>
program->kanban->doingProjects;?>program->kanban->doingExecutions;?>
name;?>>name;?>name;?> rowspan='rowspan;?>'>name;?> -
- plans as $planID => $plan):?> -
-
-
- createLink('productplan', 'view', "planID=$plan->id"), $plan->title);?> -
-
-
- -
-
-
- projects['wait'])):?> - projects['wait'] as $projectID => $project):?> -
-
-
- createLink('project', 'index', "projectID=$project->id"), $project->name);?> -
-
-
- - -
-
-
-
> -
-
- createLink('project', 'index', "projectID=$project->id"), $project->name);?> -
-
-
- hours->progress) ? $project->hours->progress : 0;?> -
-
-
-
-
-
-
-
-
- execution)):?> -
-
> -
-
- createLink('execution', 'task', "executionID={$project->execution->id}"), $project->execution->name);?> -
-
-
- execution->hours->progress) ? $project->execution->hours->progress : 0;?> -
-
-
-
-
-
-
-
- -
rowspan;?>> -
- releases as $releaseID => $release):?> -
-
-
- marker ? " " : '';?> - createLink('release', 'view', "releaseID=$release->id"), $release->name . $flag);?> -
-
-
- -
-
-
+ + +
+

noData;?>

+
+ +
+ $programs):?> + +
+
+ program->kanban->typeList[$type];?> +
+
+
- +
- + $lang->program->kanban->openProducts, 'type' => 'unclosedProduct'); +$kanbanColumns['unexpiredPlan'] = array('name' => $lang->program->kanban->unexpiredPlans, 'type' => 'unexpiredPlan'); +$kanbanColumns['waitProject'] = array('name' => $lang->program->kanban->waitingProjects, 'type' => 'waitProject'); +$kanbanColumns['doingProject'] = array('name' => $lang->program->kanban->doingProjects, 'type' => 'doingProject'); +$kanbanColumns['doingExecution'] = array('name' => $lang->program->kanban->doingExecutions, 'type' => 'doingExecution'); +$kanbanColumns['normalRelease'] = array('name' => $lang->program->kanban->normalReleases, 'type' => 'normalRelease'); +$userPrivs = array(); +$userPrivs['product'] = common::hasPriv('product', 'browse'); +$userPrivs['productplan'] = common::hasPriv('productplan', 'view'); +$userPrivs['project'] = common::hasPriv('project', 'index'); +$userPrivs['execution'] = common::hasPriv('execution', 'task'); +$userPrivs['release'] = common::hasPriv('release', 'view'); +js::set('kanbanColumns', $kanbanColumns); +js::set('userPrivs', $userPrivs); +js::set('kanbanGroup', $kanbanGroup); +js::set('doingText', $lang->program->statusList['doing']); +?> + diff --git a/module/project/js/kanban.js b/module/project/js/kanban.js index 588428400d..091dd94671 100644 --- a/module/project/js/kanban.js +++ b/module/project/js/kanban.js @@ -1,27 +1,66 @@ +/** + * Process kanban data + * @param {string} key Kanban key, used as kanban id + * @param {Object} programGroup Group data + * @returns {Object} kanban data + */ +function processKanbanData(key, programGroup) +{ + var kanbanId = key; + + /* Generate columns */ + var columns = []; + $.each(kanbanColumns, function(_, column) + { + columns.push($.extend({}, column, + { + kanban: kanbanId, + id: kanbanId + '-' + column.type, + })); + }); + /* Format lanes data */ + var lanes = []; + $.each(programGroup, function(programId, statusMap) + { + var programName = programPairs[programId]; + var items = {doingExecution: []}; + + /* Projects and executions */ + ['wait', 'doing', 'closed'].forEach(function(status) + { + var itemsList = []; + var statusProjects = statusMap[status]; + if(statusProjects) + { + $.each(statusProjects, function(projectID, project) + { + itemsList.push($.extend({}, project, {id: 'project-' + projectID, _id: projectID})); + + if(status === 'doing') + { + var execution = latestExecutions[projectID]; + if(execution) + { + var executionItem = $.extend({}, execution, {id: 'execution-' + execution.id, _id: execution.id}); + items.doingExecution = [executionItem]; + } + } + }); + } + items[status + 'Project'] = itemsList; + }); + + lanes.push({id: programId, kanban: kanbanId, name: programName, items: items}); + }); + + return {id: kanbanId, columns: columns, lanes: lanes}; +} + $(function() { - $("div[class^='board-doing-']").height($('.board-doing-project').height()); - - $('.board-program').each(function() + /* Init all kanbans */ + $.each(kanbanGroup, function(key, programGroup) { - var boardWaitCount = $(this).find('.board-wait .board-item').length; - var boardClosedCount = $(this).find('.board-closed .board-item').length; - var boardDoingCount = $(this).find('.board-doing-project .board-item').length; - - if((boardWaitCount > 5 || boardClosedCount > 5) && (boardWaitCount > boardDoingCount || boardClosedCount > boardDoingCount)) - { - var boardHeight = 0; - if(boardDoingCount >= 5) - { - boardHeight = $('.board-doing-project').outerHeight(true) * boardDoingCount; - } - else - { - var boardHeight = $(this).find('.board-project .board-item').outerHeight(true) * 5; - } - - $(this).find('.board-project').css("height", boardHeight); - $(this).find('.board-project').css("overflow", 'auto'); - } + $('#kanban-' + key).kanban({data: processKanbanData(key, programGroup)}); }); -}) +}); diff --git a/module/project/view/kanban.html.php b/module/project/view/kanban.html.php index ccc3390293..c5b32e74db 100644 --- a/module/project/view/kanban.html.php +++ b/module/project/view/kanban.html.php @@ -1,144 +1,48 @@ + * @package ZenTaoPMS + * @version $Id */ ?> - + -
-

project->empty;?>

+
+

project->empty;?>

- $projectGroup):?> - -
-
-
-
project->typeList[$type];?>
-
- - - - - - - - - - - - - - - $statusList):?> - - - - - - - - - - - - - 9) $colorIndex = 0;?> - - -
project->waitProjects;?>project->statusList['doing'];?>project->closedProjects;?>
project->doingProjects;?>project->doingExecutions;?>
- - -
-
> -
-
- createLink('project', 'index', "projectID=$project->id"), $project->name, '', "title='{$project->name}'"); - } - else - { - echo "{$project->name}"; - } - ?> -
-
-
-
-
hours->progress);?>
-
-
-
-
-
-
- - -
- - -
- id])):?> - id];?> -
> -
-
- createLink('execution', 'task', "executionID=$execution->id"), $execution->name, '', "title='{$execution->name}'"); - } - else - { - echo "{$execution->name}"; - } - ?> -
-
-
-
-
hours->progress);?>
-
-
-
-
-
- -
- - -
-
- - -
> - createLink('project', 'index', "projectID=$project->id"), $project->name, '', "title='{$project->name}'"); - } - else - { - echo "{$project->name}"; - } - ?> -
- - -
-
-
+
+ $projectGroup):?> +
+
+ project->typeList[$type];?> +
+
+
+
- - + $lang->project->waitProjects, 'type' => 'waitProject'); +$kanbanColumns['doingProject'] = array('name' => $lang->project->doingProjects, 'type' => 'doingProject'); +$kanbanColumns['doingExecution'] = array('name' => $lang->project->doingExecutions, 'type' => 'doingExecution'); +$kanbanColumns['closedProject'] = array('name' => $lang->project->closedProjects, 'type' => 'closedProject'); +$userPrivs = array(); +$userPrivs['project'] = common::hasPriv('project', 'index'); +$userPrivs['execution'] = common::hasPriv('execution', 'task'); +js::set('kanbanColumns', $kanbanColumns); +js::set('userPrivs', $userPrivs); +js::set('kanbanGroup', $kanbanGroup); +js::set('latestExecutions', $latestExecutions); +js::set('programPairs', $programPairs); +js::set('doingText', $lang->project->statusList['doing']); +?> +