diff --git a/module/common/model.php b/module/common/model.php
index 504f4c49a8..5e8f8b5cd1 100644
--- a/module/common/model.php
+++ b/module/common/model.php
@@ -538,6 +538,147 @@ class commonModel extends model
}
}
+ /**
+ * Print top bar for ZIN demo.
+ *
+ * @static
+ * @access public
+ * @return array
+ */
+ public static function printUserBarZin()
+ {
+ global $lang, $app;
+
+ $menus = array();
+
+ if(isset($app->user))
+ {
+ $isGuest = $app->user->account == 'guest';
+
+ if(!$isGuest)
+ {
+ $menus[] = array
+ (
+ 'url' => helper::createLink('my', 'profile', '', '', true),
+ 'avatar' => $app->user->avatar,
+ 'text' => empty($app->user->realname) ? $app->user->account : $app->user->realname,
+ 'role' => isset($lang->user->roleList[$app->user->role]) ? $lang->user->roleList[$app->user->role] : '',
+ );
+ $menus[] = array('text' => 'divider');
+
+ $vision = $app->config->vision == 'lite' ? 'rnd' : 'lite';
+
+ $menus[] = array
+ (
+ 'url' => helper::createLink('my', 'profile', '', '', true),
+ 'icon' => 'account',
+ 'text' => $lang->profile,
+ );
+
+ if($app->config->vision === 'rnd')
+ {
+ if(!commonModel::isTutorialMode())
+ {
+ $menus[] = array
+ (
+ 'url' => helper::createLink('tutorial', 'start'),
+ 'icon' => 'guide',
+ 'text' => $lang->tutorialAB,
+ );
+ }
+
+ $menus[] = array
+ (
+ 'url' => helper::createLink('my', 'preference', 'showTip=false', '', true),
+ 'icon' => 'controls',
+ 'text' => $lang->preference,
+ );
+ }
+
+ if(common::hasPriv('my', 'changePassword'))
+ {
+ $menus[] = array
+ (
+ 'url' => helper::createLink('my', 'changepassword', '', '', true),
+ 'icon' => 'cog-outline',
+ 'text' => $lang->changePassword,
+ );
+ }
+
+ $menus[] = array('text' => 'divider');
+ }
+
+ $items = array();
+ foreach($app->lang->themes as $key => $value)
+ {
+ $item = array
+ (
+ 'url' => "javascript:selectTheme(\"$key\")",
+ 'text' => $value,
+ 'attr' => array('data-value' => $key),
+ );
+ if($app->cookie->theme == $key) $item['attr']['class'] = 'selected';
+ $items[] = $item;
+ }
+ $menus[] = array
+ (
+ 'url' => 'javascript:;',
+ 'icon' => 'theme',
+ 'text' => $lang->theme,
+ 'items'=> $items,
+ );
+
+ $items = array();
+ foreach($app->config->langs as $key => $value)
+ {
+ $item = array
+ (
+ 'url' => "javascript:selectLang(\"$key\")",
+ 'text' => $value,
+ 'attr' => array('data-value' => $key),
+ );
+ if($app->cookie->lang == $key) $item['attr']['class'] = 'selected';
+ $items[] = $item;
+ }
+ $menus[] = array
+ (
+ 'url' => 'javascript:;',
+ 'icon' => 'lang',
+ 'text' => $lang->lang,
+ 'items'=> $items,
+ );
+
+ //if(!$isGuest and !commonModel::isTutorialMode() and $app->viewType != 'mhtml')
+ //{
+ // $customLink = helper::createLink('custom', 'ajaxMenu', "module={$app->getModuleName()}&method={$app->getMethodName()}", '', true);
+ // echo "
$lang->customMenu";
+ //}
+
+ //commonModel::printAboutBar();
+ $menus[] = array('text' => 'divider');
+ if($isGuest)
+ {
+ $menus[] = array
+ (
+ 'url' => helper::createLink('user', 'login'),
+ 'text' => $lang->login,
+ );
+ }
+ else
+ {
+ $menus[] = array
+ (
+ 'url' => helper::createLink('user', 'logout'),
+ 'icon' => 'exit',
+ 'text' => $lang->logout,
+ );
+ }
+
+ }
+
+ return $menus;
+ }
+
/**
* Print vision switcher.
*
@@ -576,6 +717,165 @@ class commonModel extends model
}
}
+ /**
+ * Print create button list for Zin Demo.
+ *
+ * @static
+ * @access public
+ * @return void
+ */
+ public static function printCreateListZin()
+ {
+ global $app, $config, $lang;
+
+ $menus = array();
+
+ /* Initialize the default values. */
+ $showCreateList = $needPrintDivider = false;
+
+ /* Get default product id. */
+ $productID = isset($_SESSION['product']) ? $_SESSION['product'] : 0;
+ if($productID)
+ {
+ $product = $app->dbh->query("SELECT id FROM " . TABLE_PRODUCT . " WHERE `deleted` = '0' and vision = '{$config->vision}' and id = '{$productID}'")->fetch();
+ if(empty($product)) $productID = 0;
+ }
+ if(!$productID and $app->user->view->products)
+ {
+ $product = $app->dbh->query("SELECT id FROM " . TABLE_PRODUCT . " WHERE `deleted` = '0' and vision = '{$config->vision}' and id " . helper::dbIN($app->user->view->products) . " order by `order` desc limit 1")->fetch();
+ if($product) $productID = $product->id;
+ }
+
+ if($config->vision == 'lite')
+ {
+ $condition = " WHERE `deleted` = '0' AND `vision` = 'lite' AND `model` = 'kanban'";
+ if(!$app->user->admin) $condition .= " AND `id` " . helper::dbIN($app->user->view->projects);
+
+ $object = $app->dbh->query("select id from " . TABLE_PROJECT . $condition . ' LIMIT 1')->fetch();
+ if(empty($object)) unset($lang->createIcons['story'], $lang->createIcons['task'], $lang->createIcons['execution']);
+ }
+
+ if($config->edition == 'open') unset($lang->createIcons['effort']);
+ if($config->systemMode == 'light') unset($lang->createIcons['program']);
+
+ /* Check whether the creation permission is available, and print create buttons. */
+
+ foreach($lang->createIcons as $objectType => $objectIcon)
+ {
+ $createMethod = 'create';
+ $module = $objectType == 'kanbanspace' ? 'kanban' : $objectType;
+ if($objectType == 'effort') $createMethod = 'batchCreate';
+ if($objectType == 'kanbanspace') $createMethod = 'createSpace';
+ if(strpos('|bug|execution|kanbanspace|', "|$objectType|") !== false) $needPrintDivider = true;
+
+ if(common::hasPriv($module, $createMethod))
+ {
+ if($objectType == 'doc' and !common::hasPriv('doc', 'tableContents')) continue;
+
+ /* Determines whether to print a divider. */
+ if($needPrintDivider and $showCreateList)
+ {
+ $menus[] = array('text' => 'divider');
+ $needPrintDivider = false;
+ }
+
+ $showCreateList = true;
+ $isOnlyBody = false;
+ $attr = array();
+
+ $params = '';
+ switch($objectType)
+ {
+ case 'doc':
+ $params = "objectType=&objectID=0&libID=0";
+ $createMethod = 'selectLibType';
+ $isOnlyBody = true;
+ $attr = array('class'=>'iframe', 'data-width'=>'700px');
+ break;
+ case 'project':
+ if($config->vision == 'lite')
+ {
+ $params = "model=kanban";
+ }
+ else if(!defined('TUTORIAL'))
+ {
+ $params = "programID=0&from=global";
+ $createMethod = 'createGuide';
+ $attr = array('data-toggle'=>'modal');
+ }
+ else
+ {
+ $params = "model=scrum&programID=0©ProjectID=0&extra=from=global";
+ }
+
+ break;
+ case 'bug':
+ $params = "productID=$productID&branch=&extras=from=global";
+ break;
+ case 'story':
+ if(!$productID and $config->vision == 'lite')
+ {
+ $module = 'project';
+ $params = "model=kanban";
+ }
+ else
+ {
+ $params = "productID=$productID&branch=0&moduleID=0&storyID=0&objectID=0&bugID=0&planID=0&todoID=0&extra=from=global";
+ if($config->vision == 'lite')
+ {
+ $projectID = isset($_SESSION['project']) ? $_SESSION['project'] : 0;
+ $projects = $app->dbh->query("SELECT t2.id FROM " . TABLE_PROJECTPRODUCT . " AS t1 LEFT JOIN " . TABLE_PROJECT . " AS t2 ON t1.project = t2.id WHERE t1.`product` = '{$productID}' and t2.`type` = 'project' and t2.id " . helper::dbIN($app->user->view->projects) . " ORDER BY `order` desc")->fetchAll();
+
+ $projectIdList = array();
+ foreach($projects as $project) $projectIdList[$project->id] = $project->id;
+ if($projectID and !isset($projectIdList[$projectID])) $projectID = 0;
+ if(empty($projectID)) $projectID = key($projectIdList);
+
+ $params = "productID={$productID}&branch=0&moduleID=0&storyID=0&objectID={$projectID}&bugID=0&planID=0&todoID=0&extra=from=global";
+ }
+ }
+
+ break;
+ case 'task':
+ $params = "executionID=0&storyID=0&moduleID=0&taskID=0&todoID=0&extra=from=global";
+ break;
+ case 'testcase':
+ $params = "productID=$productID&branch=&moduleID=0&from=¶m=0&storyID=0&extras=from=global";
+ break;
+ case 'execution':
+ $projectID = isset($_SESSION['project']) ? $_SESSION['project'] : 0;
+ $params = "projectID={$projectID}&executionID=0©ExecutionID=0&planID=0&confirm=no&productID=0&extra=from=global";
+ break;
+ case 'product':
+ $params = "programID=&extra=from=global";
+ break;
+ case 'program':
+ $params = "parentProgramID=0&extra=from=global";
+ break;
+ case 'kanbanspace':
+ $isOnlyBody = true;
+ $attr = array('class'=>'iframe', 'data-width'=>'75%');
+ break;
+ case 'kanban':
+ $isOnlyBody = true;
+ $attr = array('class'=>'iframe', 'data-width'=>'75%');
+ break;
+ }
+
+ $menus[] = array(
+ 'url' => helper::createLink($module, $createMethod, $params, '', $isOnlyBody),
+ 'icon' => $objectIcon,
+ 'text' => $lang->createObjects[$objectType],
+ 'attr' => $attr
+ );
+ }
+ }
+
+ if(!$showCreateList) return '';
+
+ return $menus;
+ }
+
/**
* Print create button list.
*
diff --git a/module/program/ui/browse.html.php b/module/program/ui/browse.html.php
index e38b3e9d6e..df118ea7e3 100644
--- a/module/program/ui/browse.html.php
+++ b/module/program/ui/browse.html.php
@@ -14,6 +14,13 @@ foreach(\customModel::getMainMenu() as $menuItem)
);
}
+/* Generate dropdown menu. */
+$shotcutAddMenu = dropdown(setId('shortcutAdd'));
+foreach(\commonModel::printCreateListZin() as $item) $shotcutAddMenu->append(item($item));
+
+$userMenu = dropdown(setId('userMenu'));
+foreach(\commonModel::printUserBarZin() as $item) $userMenu->append(item($item));
+
page
(
set('title', $title),
@@ -27,12 +34,9 @@ page
),
pagenavbar
(
- zuinav
- (
- set('js-render', false),
- set('items', $navItems),
- item(array('text' => 'text')),
- ),
+ set('js-render', false),
+ set('items', $navItems),
+ item(array('text' => 'text')),
),
toolbar
(
@@ -41,12 +45,28 @@ page
item(array('icon' => 'icon-plus')),
item(array('icon' => 'icon-group')),
item(array('text' => '研发综合界面')),
+ btn
+ (
+ icon('plus'),
+ setClass('rounded-sm btn square size-sm secondary'),
+ set('data-toggle', 'dropdown'),
+ set('data-trigger', 'hover'),
+ set('data-arrow', 'true'),
+ set('href', '#shortcutAdd'),
+ ),
+ avatar
+ (
+ set('role', '研发'),
+ set('avatar', '/data/upload/1/202302/07134647027036pb'),
+ set('href', '#userMenu'),
+ ),
),
),
-
pagemain(
),
+ $shotcutAddMenu,
+ $userMenu,
);
/*
diff --git a/zin/wg.func.php b/zin/wg.func.php
index cc8ed0b1d7..987c9ef45c 100755
--- a/zin/wg.func.php
+++ b/zin/wg.func.php
@@ -30,11 +30,11 @@ function data()
return core\data::$global;
}
-function btn() {return createWg('btn', func_get_args());}
-function icon() {return createWg('icon', func_get_args());}
-function pagebase() {return createWg('pagebase', func_get_args());}
-function page() {return createWg('page', func_get_args());}
-function dtable() {return createWg('dtable', func_get_args());}
+function btn() {return createWg('btn', func_get_args());}
+function icon() {return createWg('icon', func_get_args());}
+function pagebase() {return createWg('pagebase', func_get_args());}
+function page() {return createWg('page', func_get_args());}
+function dtable() {return createWg('dtable', func_get_args());}
function pageheader() {return createWg('pageheader', func_get_args());}
function pageheading() {return createWg('pageheading', func_get_args());}
function pagenavbar() {return createWg('pagenavbar', func_get_args());}
@@ -42,3 +42,4 @@ function zuinav() {return createWg('zuinav', func_get_args());}
function toolbar() {return createWg('toolbar', func_get_args());}
function pagemain() {return createWg('pagemain', func_get_args());}
function dropdown() {return createWg('dropdown', func_get_args());}
+function avatar() {return createWg('avatar', func_get_args());}
diff --git a/zin/wg/avatar/v1.php b/zin/wg/avatar/v1.php
new file mode 100644
index 0000000000..f688b88a94
--- /dev/null
+++ b/zin/wg/avatar/v1.php
@@ -0,0 +1,85 @@
+ 'userMenu dropdown-toggle',
+ 'data-arrow' => true,
+ 'data-toggle' => 'dropdown',
+ 'data-trigger' => 'hover',
+ 'data-placement' => 'bottom',
+ 'href' => '#userMenu',
+ 'theme' => 'success',
+ 'radius' => 'circle',
+ 'size' => '',
+ 'outline' => '',
+ 'showName' => false,
+ );
+
+ static $customProps = 'text,theme,radius,size,outline,name,role';
+
+ protected function acceptChild($child, $strAsHtml = false)
+ {
+ $child = parent::acceptChild($child, $strAsHtml);
+
+ if(!$strAsHtml && is_string($child) && !$this->props->has('name'))
+ {
+ $this->prop('name', $child);
+ return NULL;
+ }
+ return $child;
+ }
+
+ protected function build($isPrint = false, $parent = NULL)
+ {
+ $builder = parent::build($isPrint, $parent);
+
+ /* Strig avatar. */
+ if($this->hasProp('name'))
+ {
+ $div = h5::div($this->prop('name'))->addClass('avatar');
+ if(!empty($this->prop('radius'))) $div->addClass($this->prop('radius'));
+ if(!empty($this->prop('theme'))) $div->addClass($this->prop('theme'));
+ if(!empty($this->prop('outline'))) $div->addClass($this->prop('outline') . '-outline');
+ if(!empty($this->prop('size'))) $div->addClass($this->prop('size'));
+ $builder->append($div);
+
+ if(!empty($this->prop('showName')))
+ {
+ $builder->append(h5::span($this->prop('name')));
+ if(!empty($this->prop('role'))) $builder->append(h5::span($this->prop('role')));
+ }
+
+ return $builder;
+ }
+
+ if(!$this->hasProp('avatar')) return $builder;
+
+ /* Image avatar. */
+ $div = h5::div(h5::img(\zin\set('src', $this->prop('avatar'))))->addClass('avatar');
+ if(!empty($this->prop('radius'))) $div->addClass($this->prop('radius'));
+ if(!empty($this->prop('theme'))) $div->addClass($this->prop('theme'));
+ if(!empty($this->prop('outline'))) $div->addClass($this->prop('outline') . '-outline');
+ if(!empty($this->prop('size'))) $div->addClass($this->prop('size'));
+ $builder->append($div);
+
+ /* Show name and role. */
+ if(!empty($this->prop('showName')))
+ {
+ $builder->append(h5::span($this->prop('name')));
+ if(!empty($this->prop('role'))) $builder->append(h5::span($this->prop('role')));
+ }
+
+ return $builder;
+ }
+}
+
diff --git a/zin/wg/dropdown/v1.php b/zin/wg/dropdown/v1.php
index 74aa01135e..f9f76647c5 100644
--- a/zin/wg/dropdown/v1.php
+++ b/zin/wg/dropdown/v1.php
@@ -26,12 +26,28 @@ class dropdown extends \zin\core\wg
return $li;
}
+ $li = h5::li()->addClass('menu-item');
+
+ /* Divider. */
+ if(isset($item['text']) and ($item['text'] === '|' or $item['text'] === 'divider'))
+ {
+ $li->addClass('menu-divider');
+ return $li;
+ }
+
$a = h5::a();
- if(isset($item['url'])) $a->prop('href', $item['url']);
+
+ /* Avatar item. */
+ if(isset($item['avatar']))
+ {
+ $div = h5::div()->addClass('avatar')->addClass('avatar circle primary');
+ $div->append(h5::img(\zin\set('src', $item['avatar'])));
+ $a->append($div);
+ }
+ if(isset($item['url'])) $a->prop('href', $item['url']);
if(isset($item['icon'])) $a->append(h5::i()->addClass("icon icon-{$item['icon']}"));
if(isset($item['text'])) $a->append($item['text']);
- $li = h5::li()->addClass('menu-item');
$li->append($a);
return $li;