diff --git a/module/program/config.php b/module/program/config.php index 3e7476b230..a9759c3b1c 100644 --- a/module/program/config.php +++ b/module/program/config.php @@ -200,3 +200,88 @@ $config->program->productView->dtable->fieldList['releaseCount']['title'] = $ $config->program->productView->dtable->fieldList['releaseCount']['width'] = 80; $config->program->productView->dtable->fieldList['releaseCount']['type'] = 'html'; $config->program->productView->dtable->fieldList['releaseCount']['sortType'] = false; + +/* DataTable fields of Project View. */ +$config->program->projectView = new stdClass(); +$config->program->projectView->dtable = new stdClass(); +$config->program->projectView->dtable->fieldList = array(); + +$config->program->projectView->dtable->fieldList['name']['name'] = 'name'; +$config->program->projectView->dtable->fieldList['name']['title'] = $lang->nameAB; +$config->program->projectView->dtable->fieldList['name']['width'] = 200; +$config->program->projectView->dtable->fieldList['name']['type'] = 'link'; +$config->program->projectView->dtable->fieldList['name']['flex'] = 1; +$config->program->projectView->dtable->fieldList['name']['nestedToggle'] = true; +$config->program->projectView->dtable->fieldList['name']['checkbox'] = true; +$config->program->projectView->dtable->fieldList['name']['sortType'] = true; +$config->program->projectView->dtable->fieldList['name']['iconRender'] = 'RAWJSRAWJS'; + +$config->program->projectView->dtable->fieldList['status']['name'] = 'status'; +$config->program->projectView->dtable->fieldList['status']['title'] = $lang->program->status; +$config->program->projectView->dtable->fieldList['status']['minWidth'] = 60; +$config->program->projectView->dtable->fieldList['status']['type'] = 'status'; +$config->program->projectView->dtable->fieldList['status']['sortType'] = true; +$config->program->projectView->dtable->fieldList['status']['statusMap'] = $lang->program->statusList; + +$config->program->projectView->dtable->fieldList['PMName']['name'] = 'PMName'; +$config->program->projectView->dtable->fieldList['PMName']['title'] = $lang->program->PM; +$config->program->projectView->dtable->fieldList['PMName']['minWidth'] = 80; +$config->program->projectView->dtable->fieldList['PMName']['type'] = 'avatarBtn'; +$config->program->projectView->dtable->fieldList['PMName']['sortType'] = true; + +$config->program->projectView->dtable->fieldList['budget']['name'] = 'budget'; +$config->program->projectView->dtable->fieldList['budget']['title'] = $lang->program->budget; +$config->program->projectView->dtable->fieldList['budget']['width'] = 90; +$config->program->projectView->dtable->fieldList['budget']['type'] = 'format'; +$config->program->projectView->dtable->fieldList['budget']['sortType'] = true; + +$config->program->projectView->dtable->fieldList['invested']['name'] = 'invested'; +$config->program->projectView->dtable->fieldList['invested']['title'] = $lang->program->invested; +$config->program->projectView->dtable->fieldList['invested']['minWidth'] = 70; +$config->program->projectView->dtable->fieldList['invested']['type'] = 'format'; +$config->program->projectView->dtable->fieldList['invested']['sortType'] = true; + +$config->program->projectView->dtable->fieldList['begin']['name'] = 'begin'; +$config->program->projectView->dtable->fieldList['begin']['title'] = $lang->program->begin; +$config->program->projectView->dtable->fieldList['begin']['minWidth'] = 90; +$config->program->projectView->dtable->fieldList['begin']['type'] = 'datetime'; +$config->program->projectView->dtable->fieldList['begin']['sortType'] = true; + +$config->program->projectView->dtable->fieldList['end']['name'] = 'end'; +$config->program->projectView->dtable->fieldList['end']['title'] = $lang->program->end; +$config->program->projectView->dtable->fieldList['end']['minWidth'] = 90; +$config->program->projectView->dtable->fieldList['end']['type'] = 'datetime'; +$config->program->projectView->dtable->fieldList['end']['sortType'] = true; + +$config->program->projectView->dtable->fieldList['progress']['name'] = 'progress'; +$config->program->projectView->dtable->fieldList['progress']['title'] = $lang->program->progressAB; +$config->program->projectView->dtable->fieldList['progress']['minWidth'] = 100; +$config->program->projectView->dtable->fieldList['progress']['type'] = 'circleProgress'; + +$config->program->projectView->dtable->fieldList['actions']['name'] = 'actions'; +$config->program->projectView->dtable->fieldList['actions']['title'] = $lang->actions; +$config->program->projectView->dtable->fieldList['actions']['width'] = 160; +$config->program->projectView->dtable->fieldList['actions']['type'] = 'actions'; +$config->program->projectView->dtable->fieldList['actions']['fixed'] = 'right'; +$config->program->projectView->dtable->fieldList['actions']['actionsMap'] = array( + 'program_start' => array('icon' => 'icon-start', 'hint' => $lang->program->start), + 'program_suspend' => array('icon' => 'icon-pause', 'hint' => $lang->program->suspend), + 'program_close' => array('icon' => 'icon-off', 'hint' => $lang->program->close), + 'program_activate' => array('icon' => 'icon-active', 'hint' => $lang->program->activate), + 'program_other' => array('caret' => true, 'hint' => $lang->program->other, 'type' => 'dropdown', ), + 'program_edit' => array('icon' => 'icon-edit', 'hint' => $lang->program->edit), + 'program_create' => array('icon' => 'icon-split', 'hint' => $lang->program->create), + 'program_delete' => array('icon' => 'icon-trash', 'hint' => $lang->program->delete), + 'project_start' => array('icon' => 'icon-start', 'hint' => $lang->project->start), + 'project_suspend' => array('icon' => 'icon-pause', 'hint' => $lang->project->suspend), + 'project_close' => array('icon' => 'icon-off', 'hint' => $lang->project->close), + 'project_activate' => array('icon' => 'icon-active', 'hint' => $lang->project->activate), + 'project_other' => array('caret' => true, 'hint' => $lang->project->other, 'type' => 'dropdown', ), + 'project_edit' => array('icon' => 'icon-edit', 'hint' => $lang->project->edit), + 'project_team' => array('icon' => 'icon-groups', 'hint' => $lang->project->manageMembers), + 'project_group' => array('icon' => 'icon-lock', 'hint' => $lang->project->group), + 'project_more' => array('icon' => 'icon-ellipsis-v', 'hint' => $lang->project->moreActions, 'type' => 'dropdown', 'caret' => false), + 'project_link' => array('icon' => 'icon-link', 'hint' => $lang->project->manageProducts), + 'project_whitelist' => array('icon' => 'icon-shield-check', 'hint' => $lang->project->whitelist), + 'project_delete' => array('icon' => 'icon-trash', 'hint' => $lang->project->delete) +); diff --git a/module/program/control.php b/module/program/control.php index 165ef2528c..e1e22dbcc8 100644 --- a/module/program/control.php +++ b/module/program/control.php @@ -780,11 +780,109 @@ class program extends control $this->loadModel('setting')->updateItem("{$this->app->user->account}.program.showAllProjects", $data->showAllProjects); } - public function projectView() + /** + * Product View list. + * copied from browse() + * + * @param string $status + * @param string $orderBy + * @param int $recTotal + * @param int $recPerPage + * @param int $pageID + * @param int $param + * @access public + * @return void + */ + public function projectView($status = 'unclosed', $orderBy = 'order_asc', $recTotal = 0, $recPerPage = 10, $pageID = 1, $param = 0) { - echo 'projectView'; + if(common::hasPriv('program', 'create')) $this->lang->pageActions = html::a($this->createLink('program', 'create'), " " . $this->lang->program->create, '', "class='btn btn-primary create-program-btn'"); + + $this->session->set('programList', $this->app->getURI(true), 'program'); + $this->session->set('projectList', $this->app->getURI(true), 'program'); + $this->session->set('createProjectLocate', $this->app->getURI(true), 'program'); + + $this->app->loadClass('pager', $static = true); + $pager = new pager($recTotal, $recPerPage, $pageID); + + $programType = $this->cookie->programType ? $this->cookie->programType : 'bylist'; + + if($programType === 'bygrid') + { + $programs = $this->program->getProgramStats($status, 20, $orderBy); + } + else + { + if(strtolower($status) == 'bysearch') + { + $queryID = (int)$param; + $programs = $this->program->getListBySearch($orderBy, $queryID); + } + else + { + /* Get top programs and projects. */ + $topObjects = $this->program->getList($status == 'unclosed' ? 'doing,suspended,wait' : $status, $orderBy, $pager, 'top'); + if(!$topObjects) $topObjects = array(0); + $programs = $this->program->getList($status == 'closed' ? 'closed' : 'all', $orderBy, NULL, 'child', array_keys($topObjects)); + + /* Get summary. */ + $topCount = $indCount = 0; + foreach($programs as $program) + { + if($program->type == 'program' and $program->parent == 0) $topCount ++; + if($program->type == 'project' and $program->parent == 0) $indCount ++; + } + $summary = sprintf($this->lang->program->summary, $topCount, $indCount); + } + } + + /* Get PM id list. */ + $accounts = array(); + $hasProject = false; + foreach($programs as $program) + { + if(!empty($program->PM) and !in_array($program->PM, $accounts)) $accounts[] = $program->PM; + if($hasProject === false and $program->type != 'program') $hasProject = true; + } + $PMList = $this->loadModel('user')->getListByAccounts($accounts, 'account'); + + /* Build the search form. */ + $actionURL = $this->createLink('program', 'browse', "status=bySearch&orderBy={$orderBy}&recTotal={$recTotal}&recPerPage={$recPerPage}&pageID={$pageID}¶m=myQueryID"); + $this->config->program->search['actionURL'] = $actionURL; + $this->loadModel('search')->setSearchParams($this->config->program->search); + + $this->view->title = $this->lang->program->browse; + $this->view->position[] = $this->lang->program->browse; + + $this->view->programs = $programs; + $this->view->status = $status; + $this->view->orderBy = $orderBy; + $this->view->summary = isset($summary) ? $summary : ''; + $this->view->pager = $pager; + $this->view->users = $this->user->getPairs('noletter'); + $this->view->userIdPairs = $this->user->getPairs('noletter|showid'); + $this->view->usersAvatar = $this->user->getAvatarPairs(''); + $this->view->programType = $programType; + $this->view->PMList = $PMList; + $this->view->progressList = $this->program->getProgressList(); + $this->view->hasProject = $hasProject; + $this->view->param = $param; + $this->view->recTotal = $pager->recTotal; + + $this->render(); } + /** + * Product View list. + * + * @param string $status + * @param string $orderBy + * @param int $recTotal + * @param int $recPerPage + * @param int $pageID + * @param int $param + * @access public + * @return void + */ public function productView($status = 'unclosed', $orderBy = 'order_asc', $recTotal = 0, $recPerPage = 10, $pageID = 1, $param = 0) { if(common::hasPriv('program', 'create')) $this->lang->pageActions = html::a($this->createLink('program', 'create'), " " . $this->lang->program->create, '', "class='btn btn-primary create-program-btn'"); diff --git a/module/program/lang/zh-cn.php b/module/program/lang/zh-cn.php index f7e027045a..f7a03a96f7 100644 --- a/module/program/lang/zh-cn.php +++ b/module/program/lang/zh-cn.php @@ -9,6 +9,7 @@ $lang->program->status = '状态'; $lang->program->PM = '负责人'; $lang->program->budget = '预算'; $lang->program->budgetUnit = '预算单位'; +$lang->program->invested = '已投入'; $lang->program->begin = '计划开始'; $lang->program->end = '计划完成'; $lang->program->realBegin = '实际开始'; @@ -83,6 +84,7 @@ $lang->program->confirmChangePRJUint = '是否同步更新该项目集下子 $lang->program->exRateNotNegative = '『汇率』不能是负数。'; $lang->program->changePRJUnit = '更新项目预算单位'; $lang->program->showNotCurrentProjects = '显示非当前项目集的项目信息'; +$lang->program->other = '其他'; $lang->program->progress = '项目进度'; $lang->program->progressAB = '进度'; @@ -122,6 +124,7 @@ $lang->program->budgetOverrun = '项目集的预算超出了父项目集的 /* ToolBar. */ $lang->program->createProduct = '添加产品'; +$lang->program->createProject = '添加项目'; /* DTable columns of product view page. */ $lang->program->unclosedReqCount = '需求未关闭'; @@ -174,6 +177,12 @@ $lang->program->featureBar['productview']['all'] = '全部'; $lang->program->featureBar['productview']['unclosed'] = '未关闭'; $lang->program->featureBar['productview']['end'] = '结束'; +$lang->program->featureBar['projectview']['all'] = '全部'; +$lang->program->featureBar['projectview']['unclosed'] = '未关闭'; +$lang->program->featureBar['projectview']['wait'] = '未开始'; +$lang->program->featureBar['projectview']['doing'] = '进行中'; +$lang->program->featureBar['projectview']['more'] = '更多'; + $lang->program->kanban = new stdclass(); $lang->program->kanban->common = '项目集看板'; $lang->program->kanban->typeList['my'] = '我参与的项目集'; @@ -189,3 +198,7 @@ $lang->program->kanban->normalReleases = '正常的发布'; $lang->program->kanban->laneColorList = array('#32C5FF', '#006AF1', '#9D28B2', '#FF8F26', '#FFC20E', '#00A78E', '#7FBB00', '#424BAC', '#C0E9FF', '#EC2761'); $lang->program->defaultProgram = '默认项目集'; + +$lang->program->projectView = '项目视角'; +$lang->program->productView = '产品视角'; +$lang->program->workerDay = '人天'; diff --git a/module/program/ui/productview.html.php b/module/program/ui/productview.html.php index bef42c56cb..1dec926080 100644 --- a/module/program/ui/productview.html.php +++ b/module/program/ui/productview.html.php @@ -12,7 +12,6 @@ $item->id = "$i"; $item->name = 'parent'; $item->type = 'program'; $item->asParent = true; -$item->parent = "0"; $item->PM = '管理员'; $item->feedback = 200; $item->unclosedReqCount = 100; @@ -181,7 +180,7 @@ window.renderReleaseCountCell = function(result, {col, row}) if(changed === 0) return result; if(changed > 0) result[0] = {html: row.data.releaseCount + ' +' + changed + ''}; - if(changed < 0) result[0] = {html: row.data.releaseCount + ' -' + changed + ''}; + if(changed < 0) result[0] = {html: row.data.releaseCount + ' ' + changed + ''}; return result; } diff --git a/module/program/ui/projectview.html.php b/module/program/ui/projectview.html.php new file mode 100644 index 0000000000..61c191e130 --- /dev/null +++ b/module/program/ui/projectview.html.php @@ -0,0 +1,141 @@ +program->projectView->dtable->fieldList); + +$data = array(); +foreach($programs as $program) +{ + if(empty($program->parent)) $program->parent = null; + + /* Delay status. */ + if($program->status != 'done' and $program->status != 'closed' and $program->status != 'suspended') + { + $delay = helper::diffDate(helper::today(), $program->end); + if($delay > 0) $program->postponed = true; + } + + /* PM. */ + if(!empty($program->PM)) + { + $userName = zget($users, $program->PM); + $program->PMNameAvatar = $usersAvatar[$program->PM]; + $program->PMName = $userName; + } + + /* Calculate budget.*/ + $programBudget = $this->loadModel('project')->getBudgetWithUnit($program->budget); + $program->budget = $program->budget != 0 ? zget($lang->project->currencySymbol, $program->budgetUnit) . ' ' . $programBudget : $lang->project->future; + + /* Progress. */ + if(isset($progressList[$program->id])) + { + $program->progress = round($progressList[$program->id]); + } + + $program->invested = 0; + + /* Actions. */ + $program->actions = array(); + $actions = $this->program->buildActions($program); + foreach($actions as $action) + { + if(is_object($action)) $action->name = $program->type . '_' . $action->name; + else $action = $program->type . '_' . $action; + if(isset($action->items)) + { + foreach($action->items as $idx => $item) + { + $action->items[$idx]->name = $program->type . '_' . $item->name; + } + } + $program->actions[] = $action; + + } + + + $data[] = $program; +} + +/* Render Cell. */ +js +( +<< x.data.type === "product").length; + const statistic = '{$summary}'.replace('%s', ' ' + count + ' '); + return [{children: statistic, className: "text-dark"}, "flex", "pager"]; +} + +window.renderCell = function(result, {col, row}) +{ + if(col.name === 'name') + { + if(row.data.postponed) result[result.length] = {html:'' + langPostponed + '', className:'flex items-end w-full', style:{flexDirection:"column"}}; + return result; + } + + if(col.name === 'budget') + { + result[0] = {html: '
' + row.data.budget + '
', className:'flex items-end w-full items-end', style:{flexDirection:"column"}}; + return result; + } + + if(col.name === 'invested') + { + result[0] = {html: '
' + row.data.invested + ' ' + langWorkerDay + '
', className:'flex items-end w-full items-end', style:{flexDirection:"column"}}; + return result; + } + + return result; +} +RENDERCELL +); + +set::title($lang->program->projectView); + +featureBar +( + set::current($status), + set::linkParams("status={key}&orderBy=$orderBy"), + (hasPriv('project', 'batchEdit') && $programType != 'bygrid' && $hasProject === true) ? item + ( + set::type('checkbox'), + set::text($lang->project->edit), + set::checked($this->cookie->editProject) + ) : NULL, + li(searchToggle()) +); + +toolbar +( + item(set(array( + 'text' => $lang->program->createProject, + 'icon' => 'plus', + 'class'=> 'btn secondary', + 'url' => createLink('program', 'exportTable') + ))), + item(set(array( + 'text' => $lang->program->create, + 'icon' => 'plus', + 'class'=> 'btn primary', + 'url' => createLink('program', 'exportTable') + ))), +); + +dtable +( + set::className('shadow rounded'), + set::cols($cols), + set::data($data), + set::footPager(usePager()), + set::nested(true), + set::onRenderCell(jsRaw('window.renderCell')), + set::footer(jsRaw('function(){return window.footerGenerator.call(this);}')) +); + +render();