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')
);