diff --git a/module/product/ui/all.html.php b/module/product/ui/all.html.php index 62db33bef2..17d27d7d3b 100644 --- a/module/product/ui/all.html.php +++ b/module/product/ui/all.html.php @@ -24,7 +24,8 @@ foreach($cols as $idx => $col) if($col['name'] == 'name') { unset($cols[$idx]['width']); - $cols[$idx]['minWidth'] = 200; + $cols[$idx]['minWidth'] = 200; + $cols[$idx]['nestedToggle'] = true; } if($col['name'] != 'actions') continue; @@ -327,19 +328,30 @@ $jsSearch = <<>>", e); + }, + "onError": function(e) { + console.log('onError>>>', e); + }, + headers: { + 'Content-type': 'application/json; charset=UTF-8' + } + } + } + ); JSSEARCH; page @@ -405,9 +417,13 @@ page modal ( setId('saveModal'), + set('type', 'none'), form ( setId('saveForm'), + setClass('validation form-group'), + set('action', \helper::createLink('search', 'saveQuery', 'product')), + set('method', 'POST'), div ( setClass('flex flex-row justify-between'), diff --git a/module/program/ui/browse.html.php b/module/program/ui/browse.html.php index 449d9fc798..3f6f6ecaf2 100644 --- a/module/program/ui/browse.html.php +++ b/module/program/ui/browse.html.php @@ -93,7 +93,7 @@ page ( set('text', $lang->{$app->tab}->common), set('icon', $app->tab), - set('url', \helper::createLink($app->tab, 'browse')), + set('url', \helper::createLink($app->tab, 'browse')) ), pagenavbar ( diff --git a/module/project/control.php b/module/project/control.php index af2a89d253..0233ada057 100755 --- a/module/project/control.php +++ b/module/project/control.php @@ -420,7 +420,13 @@ class project extends control $this->view->showBatchEdit = $this->cookie->showProjectBatchEdit; $this->view->allProjectsNum = $this->loadModel('program')->getProjectStats($programID, 'all'); - $this->display(); + if(isset($_GET['o'])) + { + $this->display(); + return; + } + + $this->render(); } /** diff --git a/module/project/model.php b/module/project/model.php index 06ab3555a2..69129a115a 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -2334,6 +2334,119 @@ class projectModel extends model } } + /** + * Print datatable cell for ZIN. + * + * @param object $col + * @param object $project + * @param array $users + * @param object $item + * @param int $programID + * @access public + * @return void + */ + public function printCellZin($col, $project, $users, &$item, $programID = 0) + { + if(!$col->show) return; + + $canOrder = common::hasPriv('project', 'updateOrder'); + $canBatchEdit = common::hasPriv('project', 'batchEdit'); + $account = $this->app->user->account; + $id = $col->id; + $projectLink = helper::createLink('project', 'index', "projectID=$project->id", '', '', $project->id); + + $title = ''; + $class = "c-$id" . (in_array($id, array('budget', 'teamCount', 'estimate', 'consume')) ? ' c-number' : ''); + + if($id == 'id') $class .= ' cell-id'; + + if($id == 'code') + { + $class .= ' c-name'; + $title = "title={$project->code}"; + } + elseif($id == 'name') + { + $class .= ' text-left'; + $title = "title='{$project->name}'"; + } + elseif($id == 'PM') + { + $class .= ' c-manager'; + } + + if($id == 'end') + { + $project->end = $project->end == LONG_TIME ? $this->lang->project->longTime : $project->end; + $class .= ' c-name'; + $title = "title='{$project->end}'"; + } + + if($id == 'budget') + { + $projectBudget = $this->getBudgetWithUnit($project->budget); + $budgetTitle = $project->budget != 0 ? zget($this->lang->project->currencySymbol, $project->budgetUnit) . ' ' . $projectBudget : $this->lang->project->future; + + $title = "title='$budgetTitle'"; + } + + if($id == 'estimate') $title = "title='{$project->hours->totalEstimate} {$this->lang->execution->workHour}'"; + if($id == 'consume') $title = "title='{$project->hours->totalConsumed} {$this->lang->execution->workHour}'"; + if($id == 'surplus') $title = "title='{$project->hours->totalLeft} {$this->lang->execution->workHour}'"; + + /* TODO attach flow cells. */ + /* if($this->config->edition != 'open') $this->loadModel('flow')->printFlowCell('project', $project, $id); */ + switch($id) + { + case 'id': + $item->id = sprintf('%03d', $project->id); + break; + case 'name': + $item->name = $project->name; + break; + case 'code': + $item->code = $project->code; + break; + case 'PM': + $item->PM = $project->PM; + break; + case 'begin': + $item->begin = $project->begin; + break; + case 'end': + $item->end = $project->end; + break; + case 'status': + $item->status = zget($this->lang->project->statusList, $project->status); + break; + case 'hasProduct': + $item->hasProduct = zget($this->lang->project->projectTypeList, $project->hasProduct); + break; + case 'budget': + $item->budget = $budgetTitle; + break; + case 'teamCount': + $item->teamCount = $project->teamCount; + break; + case 'estimate': + $item->estimate = $project->hours->totalEstimate . $this->lang->execution->workHourUnit; + break; + case 'consume': + $item->consume = $project->hours->totalConsumed . $this->lang->execution->workHourUnit; + break; + case 'surplus': + $item->surplus = $project->hours->totalLeft . $this->lang->execution->workHourUnit; + break; + case 'progress': + $item->progress = $project->hours->progress; + break; + case 'actions': + $project->programID = $programID; + $this->buildOperateMenuZin($project, $item, 'browse'); + break; + } + } + /** * Convert budget unit. * @@ -2990,6 +3103,21 @@ class projectModel extends model return $result; } + /** + * Build project action menu. + * + * @param object $project + * @param object $item + * @param string $type + * @access public + * @return string + */ + public function buildOperateMenuZin($project, &$item, $type = 'view') + { + $function = 'buildOperate' . ucfirst($type) . 'MenuZin'; + return $this->$function($project, $item); + } + /** * Build project action menu. * @@ -3034,6 +3162,52 @@ class projectModel extends model return $menu; } + /** + * Build project browse action menu. + * + * @param object $project + * @param object $item + * @access public + * @return string + */ + public function buildOperateBrowseMenuZin($project, &$item) + { + $item->actions = array(); + $moduleName = 'project'; + + if($project->status == 'wait' || $project->status == 'suspended') $item->actions[] = 'start'; + if($project->status == 'doing') $item->actions[] = 'close'; + if($project->status == 'closed') $item->actions[] = 'active'; + + if(common::hasPriv($moduleName, 'suspend') || (common::hasPriv($moduleName, 'close') && $project->status != 'doing') || (common::hasPriv($moduleName, 'activate') && $project->status != 'closed')) + { + $menu = 'pause'; + $comma = ','; + if($project->status != 'doing') $menu .= $comma . 'close'; + if($project->status != 'closed') $menu .= $comma . 'active'; + + $item->actions[] = 'other:' . $menu; + } + + $item->actions[] = 'edit'; + + if($this->config->vision != 'lite') + { + $item->actions[] = 'group'; + $item->actions[] = 'perm'; + + if(common::hasPriv($moduleName, 'manageProducts') || common::hasPriv($moduleName, 'whitelist') || common::hasPriv($moduleName, 'delete')) + { + $item->actions[] = 'more:link,whitelist,delete'; + } + return; + } + + $item->actions[] = 'group'; + $item->actions[] = 'whitelist'; + $item->actions[] = 'delete'; + } + /** * Build project browse action menu. * diff --git a/module/project/ui/browse.html.php b/module/project/ui/browse.html.php new file mode 100644 index 0000000000..c9cccaf653 --- /dev/null +++ b/module/project/ui/browse.html.php @@ -0,0 +1,361 @@ + $menuItem->text, + 'url' => \commonModel::createMenuLink($menuItem, $app->tab), + 'active' => $menuItem->order === 1, + ); +} + +/* Generate dropdown menus. */ +$userMenu = \commonModel::printUserBarZin(); +$globalCreateMenu = \commonModel::printCreateListZin(); +$switcherMenu = \commonModel::printVisionSwitcherZin(); + +\common::sortFeatureMenu(); +$statuses = array(); +foreach ($lang->project->featureBar['browse'] as $key => $text) +{ + $statuses[] = array( + 'text' => $text . (($browseType == $key) ? "({$pager->recTotal})" : ''), + 'active' => $key === $browseType, + 'url' => \helper::createLink('project', 'browse', "programID=$programID&browseType=$key"), + 'class' => $key === $browseType ? '' : 'ghost' + ); +} + +$others = array(); +if (\common::hasPriv('project', 'batchEdit')) +{ + $others[] = array( + 'text' => $lang->project->edit, + 'checked' => $this->cookie->showProjectBatchEdit ? 'checked' : null, + 'type' => 'checkbox' + ); +} + +if($browseType != 'bysearch') +{ + $others[] = array( + 'text' => $lang->project->mine, + 'checked' => $this->cookie->involved ? 'checked' : null, + 'type' => 'checkbox', + 'name' => 'involved' + ); +} +$others[] = array( + 'id' => 'bysearchTab', + 'type' => 'button', + 'icon' => 'search', + 'text' => $lang->search->common, + 'class' => 'ghost' +); + +$btnGroup = array(); +$btnGroup[] = array( + 'text' => $lang->export, + 'icon' => 'export', + 'class' => 'btn secondary', + 'url' => createLink('project', 'export', $browseType, "status=$browseType&orderBy=$orderBy"), +); +$btnGroup[] = array( + 'text' => $lang->project->create, + 'icon' => 'plus', + 'class' => 'btn primary', + 'url' => createLink('project', 'create') +); + +/* Search Form options. */ +$searchOptions = <<", "title": ">" }, + { "value": ">=", "title": ">=" }, + { "value": "<", "title": "<" }, + { "value": "<=", "title": "<=" }, + { "value": "include", "title": "包含" }, + { "value": "between", "title": "介于" }, + { "value": "notinclude", "title": "不包含" }, + { "value": "belong", "title": "从属于" } + ], + "savedQuery": [ + { "id": "1", "title": "条件11", "account": "11", + "content": [ + { "fields": "status", "control": "select", "condition": "=", "value": "doing" }, + { "fields": "openedDate", "control": "date", "condition": "=", "value": "2022-11-15" }, + { "fields": "openedBy", "control": "input", "condition": "=", "value": "" }, + { "fields": "PM", "control": "select", "condition": "!=", "value": "" }, + { "fields": "openedDate", "control": "date", "condition": "include", "value": "" }, + { "fields": "begin", "control": "date", "condition": "=", "value": "" } + ] + } + ], + "andOr": [ + { "value": "and", "title": "并且" }, + { "value": "or", "title": "或者" } + ], + "groupName": [ + "第一组", + "第二组" + ], + "savedQueryTitle": "已保存的查询条件", + "saveSearch": { + "text": "保存搜索条件", + "config": { + "data-toggle": "modal", + "href": "#saveModal", + "data-url": "/index.php?m=search&f=saveQuery&module=task&onMenuBar=yes" + } + } +} +OPTIONS; +$options = json_decode($searchOptions); + +$jsSearch = <<>>', event); + }); + }); + } + + zui.create( + "AjaxForm", + "#saveForm", + { + "js-render":true, + "rules": + { + "title": + { + "required":true, + "errMsg":"请输入保存条件名" + }, + "onLoad": function(e) { + console.log(">>>", e); + }, + "onError": function(e) { + console.log('onError>>>', e); + }, + headers: { + 'Content-type': 'application/json; charset=UTF-8' + } + } + } + ); +JSSEARCH; + +$setting = $this->datatable->getSetting('project'); +$cols = array(); +foreach($setting as $value) +{ + /* Hide columns. */ + if($value->id == 'status' and strpos(',all,bysearch,undone,', ",$browseType,") === false) $value->show = false; + if($value->id == 'teamCount' and $browseType == 'all') $value->show = false; + if(\commonModel::isTutorialMode() && ($value->id == 'PM' || $value->id == 'budget' || $value->id == 'teamCount')) $value->show = false; + + if(!$value->show) continue; + + $col = array( + 'name' => $value->id, + 'title' => $value->title, + //'width' => (is_numeric($value->width) ? intval($value->width) : $value->width) + ); + if(isset($value->minWidth)) $col['minWidth'] = $value->minWidth; + + /* Process each column. */ + switch ($value->id) + { + case 'id': + $col['checkbox'] = true; + $col['nestedToggle'] = true; + break; + case 'name': + /* TODO BUG, 'width' can not be 'auto'. */ + $col['width'] = 720; + break; + case 'progress': + $col['type'] = 'circleProgress'; + break; + case 'PM': + $col['type'] = 'avatarBtn'; + break; + case 'actions': + $col['type'] = 'actions'; + $col['width'] = 160; + /* TODO be configurable. */ + $col['actionsMap'] = array( + 'start' => array('icon'=> 'icon-start', 'hint'=> '启动项目'), + 'close' => array('icon'=> 'icon-off', 'hint'=> '关闭项目'), + 'pause' => array('icon'=> 'icon-pause', 'text'=> '挂起项目'), + 'active' => array('icon'=> 'icon-magic', 'text'=> '激活项目'), + 'edit' => array('icon'=> 'icon-edit', 'hint'=> '编辑项目'), + 'group' => array('icon'=> 'icon-group', 'hint'=> '团队成员'), + 'perm' => array('icon'=> 'icon-lock', 'hint'=> '项目权限分组'), + 'delete' => array('icon'=> 'icon-trash', 'hint'=> '删除', 'text' => '删除'), + 'other' => array('type'=> 'dropdown', 'hint'=> '其他操作', 'caret' => true), + 'link' => array('icon'=> 'icon-link', 'text'=> '关联产品', 'name' => 'link'), + 'more' => array('icon'=> 'icon-ellipsis-v', 'hint'=> '更多', 'type' => 'dropdown', 'caret' => false), + 'whitelist' => array('icon'=> 'icon-shield-check', 'text'=> '项目白名单', 'name' => 'whitelist'), + ); + break; + } + + $cols[] = $col; +} + +$data = array(); +foreach($projectStats as $project) +{ + /* TODO statistic */ + $item = new \stdClass(); + foreach($setting as $value) $this->project->printCellZin($value, $project, $users, $item, $programID); + + $data[] = $item; +} + +page +( + pageheader + ( + pageheading + ( + set('text', $lang->{$app->tab}->common), + set('icon', $app->tab), + set('url', \helper::createLink($app->tab, 'browse')) + ), + pagenavbar + ( + setId('navbar'), + set('items', $navItems) + ), + pagetoolbar + ( + set('create', array('href' => '#globalCreateMenu')), + set('switcher', array('href' => '#switcherMenu', 'text' => '研发管理界面')), + block('avatar', avatar(set('name', $app->user->account), set('avatar', $app->user->avatar), set('trigger', '#userMenu'))) + ) + ), + pagemain + ( + mainmenu + ( + set('statuses', $statuses), + set('others', $others), + set('btnGroup', $btnGroup) + ), + panel + ( + setId('searchPanel'), + setClass('mb-3'), + to('body', searchform(set($options))) + ), + dtable + ( + set('cols', $cols), + set('width', '100%'), + set('data', $data) + ) + ), + dropdown + ( + setId('userMenu'), + set('items', $userMenu) + ), + dropdown + ( + setId('globalCreateMenu'), + set('items', $globalCreateMenu) + ), + dropdown + ( + setId('switcherMenu'), + set('items', $switcherMenu) + ), + modal + ( + setId('saveModal'), + set('type', 'none'), + form + ( + setId('saveForm'), + setClass('validation form-group'), + set('action', \helper::createLink('search', 'saveQuery', 'product')), + set('method', 'POST'), + div + ( + setClass('flex flex-row justify-between'), + input + ( + setId('title'), + set('name', 'title'), + setClass('form-control w-5/12'), + set('type', 'text'), + set('placeholder', '请输入保存条件名称') + ), + checkbox + ( + setId('common'), + set('name', 'common'), + setClass('w-3/12'), + '设为公共查询条件' + ), + checkbox + ( + setId('onMenuBar'), + set('name', 'onMenuBar'), + setClass('w-3/12'), + '显示在菜单栏' + ), + btn( + setClass('w-1/12 primary'), + set('data-type', 'submit'), + '保存' + ) + ) + ) + ), + h::js($jsSearch) +); diff --git a/zin/wg/checkbox/v1.php b/zin/wg/checkbox/v1.php index 936617b65c..ee080bbd0e 100644 --- a/zin/wg/checkbox/v1.php +++ b/zin/wg/checkbox/v1.php @@ -18,12 +18,12 @@ class checkbox extends wg { return h::label ( - setClass('pl-2'), - set($this->props->skip(array_keys(static::getDefinedProps(), true))), - setClass('checkbox'), + setClass('pl-2 checkbox'), h::checkbox ( - setId($this->prop('id')) + setId($this->prop('id')), + set($this->props->skip(array_keys(static::getDefinedProps(), true))), + $this->prop('checked') ? set('checked', $this->prop('checked')) : null ), $this->prop('text') );