v1.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. <?php
  2. namespace zin;
  3. require_once dirname(__DIR__) . DS . 'btn' . DS . 'v1.php';
  4. require_once dirname(__DIR__) . DS . 'sidebar' . DS . 'v1.php';
  5. class docMenu extends wg
  6. {
  7. /**
  8. * @var mixed[]
  9. */
  10. private $modules = array();
  11. /**
  12. * @var mixed[]
  13. */
  14. private $selectedKey = array();
  15. /**
  16. * @var mixed[]
  17. */
  18. private $mineTypes = array('mine', 'view', 'collect', 'createdby', 'editedby');
  19. /**
  20. * @var mixed[]
  21. */
  22. protected static $defineProps = array(
  23. 'modules: array',
  24. 'activeKey?: int',
  25. 'settingLink?: string',
  26. 'menuLink: string',
  27. 'title?: string',
  28. 'preserve?: string|bool',
  29. 'linkParams?: string="%s"',
  30. 'libID?: int=0',
  31. 'moduleID?: int=0',
  32. 'spaceType?: string',
  33. 'objectType?: string',
  34. 'objectID?: int=0',
  35. 'hover?: bool=true',
  36. 'sortable?: array',
  37. 'onSort?: function',
  38. 'canSortTo?: function'
  39. );
  40. public static function getPageCSS()
  41. {
  42. return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
  43. }
  44. public static function getPageJS()
  45. {
  46. return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
  47. }
  48. private function buildLink($item, $releaseID = 0)
  49. {
  50. global $app;
  51. $url = zget($item, 'url', '');
  52. if(!empty($url)) return $url;
  53. $objectType = $this->objectType;
  54. if($objectType == 'project' && $item->type == 'execution') return '';
  55. if(in_array($item->type, array('apiLib', 'docLib')))
  56. {
  57. $this->libID = $item->id;
  58. $this->moduleID = 0;
  59. }
  60. if($item->type == 'module') $this->moduleID = $item->id;
  61. $linkParams = sprintf($this->linkParams, "libID={$this->libID}&moduleID={$this->moduleID}");
  62. if(in_array($this->spaceType, array('product', 'project', 'custom'))) $linkParams = "objectID={$this->objectID}&{$linkParams}";
  63. $moduleName = $this->spaceType == 'api' ? 'api' : 'doc';
  64. $methodName = '';
  65. if($this->spaceType == 'api')
  66. {
  67. $methodName = 'index';
  68. }
  69. elseif($item->type == 'annex')
  70. {
  71. $methodName = 'showFiles';
  72. $linkParams = "type={$item->objectType}&objectID={$item->objectID}";
  73. }
  74. elseif(in_array($item->type, array('text', 'word','ppt', 'excel', 'attachment')))
  75. {
  76. $methodName = 'view';
  77. $linkParams = "docID={$item->id}";
  78. }
  79. elseif($objectType == 'execution' && $app->tab == 'execution')
  80. {
  81. $moduleName = 'execution';
  82. $methodName = 'doc';
  83. }
  84. elseif($objectType == 'doctemplate')
  85. {
  86. $moduleName = 'doctemplate';
  87. $methodName = 'browse';
  88. }
  89. else
  90. {
  91. $methodName = empty($this->spaceMethod[$objectType]) ? 'teamSpace' : $this->spaceMethod[$objectType];
  92. if(in_array($objectType, $this->mineTypes))
  93. {
  94. $moduleID = $item->id;
  95. if(in_array($item->type, array('docLib', 'annex', 'api', 'execution'))) $moduleID = 0;
  96. $type = in_array(strtolower($item->type), $this->mineTypes) ? strtolower($item->type) : 'mine';
  97. $linkParams = "type={$type}&libID={$this->libID}&moduleID={$moduleID}";
  98. }
  99. if($item->type == 'module' && !empty($item->object) && $item->object == 'api')
  100. {
  101. $linkParams = str_replace(array('browseType=&', 'param=0'), array('browseType=byrelease&', "param={$this->release}"), $linkParams);
  102. }
  103. }
  104. if($releaseID)
  105. {
  106. if($this->currentModule == 'doc')
  107. {
  108. $linkParams = str_replace(array('browseType=&', 'param=0'), array('browseType=byrelease&', "param={$releaseID}"), $linkParams);
  109. }
  110. else
  111. {
  112. $moduleName = 'api';
  113. $methodName = 'index';
  114. $linkParams = "libID={$this->libID}&moduleID=0&apiID=0&version=0&release={$releaseID}";
  115. }
  116. }
  117. return helper::createLink($moduleName, $methodName, $linkParams);
  118. }
  119. /**
  120. * @param mixed[] $items
  121. * @param int $parentID
  122. */
  123. private function buildMenuTree($items, $parentID = 0)
  124. {
  125. if(empty($items)) $items = $this->modules;
  126. if(empty($items)) return array();
  127. $activeKey = $this->prop('activeKey');
  128. $sortTree = $this->prop('sortable') || $this->prop('onSort');
  129. $parentItems = array();
  130. foreach($items as $setting)
  131. {
  132. if(!is_object($setting)) continue;
  133. $setting->parentID = $parentID;
  134. $itemID = 0;
  135. if(!in_array(strtolower($setting->type), $this->mineTypes)) $itemID = $setting->id ? $setting->id : $parentID;
  136. $moduleName = ($setting->type == 'apiLib' || (isset($setting->objectType) && $setting->objectType == 'api')) ? 'api' : 'doc';
  137. $selected = isset($setting->active) ? $setting->active : ($itemID && $itemID == $activeKey);
  138. if($selected) $this->selectedKey[] = $itemID;
  139. $item = array(
  140. 'key' => $itemID,
  141. 'text' => $setting->name,
  142. 'hint' => $setting->name,
  143. 'icon' => $this->getIcon($setting),
  144. 'url' => $this->buildLink($setting),
  145. 'titleAttrs' => array('data-app' => $this->tab, 'class' => 'item-title w-full'),
  146. 'data-id' => $itemID,
  147. 'data-lib' => in_array($setting->type, array('docLib', 'apiLib')) ? $itemID : zget($setting, 'libID', ''),
  148. 'data-type' => $setting->type,
  149. 'data-objectType' => isset($setting->objectType) ? $setting->objectType : '',
  150. 'data-parent' => $setting->parentID,
  151. 'data-module' => $moduleName,
  152. 'selected' => $selected,
  153. 'actions' => $this->getActions($setting)
  154. );
  155. if($sortTree && ($setting->type == 'module' && hasPriv('doc', 'sortCatalog'))) $item['trailingIcon'] = 'move muted cursor-move';
  156. if($sortTree && (in_array($setting->type, array('docLib', 'apiLib')) && hasPriv('doc', 'sortDocLib'))) $item['trailingIcon'] = 'move muted cursor-move';
  157. $children = zget($setting, 'children', array());
  158. if(!empty($children))
  159. {
  160. $children = $this->buildMenuTree($children, $itemID);
  161. $item['items'] = $children;
  162. }
  163. $parentItems[] = $item;
  164. }
  165. return $parentItems;
  166. }
  167. private function setMenuTreeProps()
  168. {
  169. global $app, $lang;
  170. $this->lang = $lang;
  171. $this->tab = $app->tab;
  172. $this->rawModule = $app->rawModule;
  173. $this->rawMethod = $app->rawMethod;
  174. $this->currentModule = $app->moduleName;
  175. $this->release = $this->prop('release', 0);
  176. $this->libID = $this->prop('libID');
  177. $this->moduleID = $this->prop('moduleID');
  178. $this->modules = $this->prop('modules');
  179. $this->linkParams = $this->prop('linkParams', '%s');
  180. $this->spaceType = $this->prop('spaceType', '');
  181. $this->objectType = $this->prop('objectType', '');
  182. $this->objectID = $this->prop('objectID', 0);
  183. $this->spaceMethod = $this->prop('spaceMethod');
  184. if($this->rawModule == 'api' && $this->rawMethod == 'view') $this->spaceType = 'api';
  185. if(empty($this->modules['project']))
  186. {
  187. $this->setProp('items', $this->buildMenuTree(array(), $this->libID));
  188. }
  189. else
  190. {
  191. $items = array();
  192. $index = 0;
  193. foreach($this->modules as $treeType => $modules)
  194. {
  195. if($treeType == 'project')
  196. {
  197. $treeTitle = $lang->projectCommon;
  198. $treeIcon = 'project';
  199. }
  200. elseif($treeType == 'execution')
  201. {
  202. $treeTitle = $lang->execution->common;
  203. $treeIcon = 'run';
  204. }
  205. if($treeType != 'annex')
  206. {
  207. $items[] = array(
  208. 'text' => $treeTitle,
  209. 'icon' => $treeIcon,
  210. 'class' => 'project-tree-title ' . ($index > 0 ? 'border-t mt-2 pt-2' : ''),
  211. 'innerClass' => 'items-center'
  212. );
  213. }
  214. $items = array_merge($items, $this->buildMenuTree($modules, $this->libID));
  215. $index ++;
  216. }
  217. $this->setProp('items', $items);
  218. }
  219. }
  220. private function getActions($item)
  221. {
  222. $versionBtn = array();
  223. if(isset($item->versions) && $item->versions)
  224. {
  225. global $lang;
  226. $versionTitle = $lang->build->common;
  227. $versionBtn = array(
  228. 'key' => 'version',
  229. 'text' => $versionTitle,
  230. 'hint' => $versionTitle,
  231. 'className' => 'versions-list',
  232. 'type' => 'dropdown',
  233. 'dropdown' => array(
  234. 'placement' => 'bottom-end',
  235. 'items' => array()
  236. )
  237. );
  238. foreach($item->versions as $version)
  239. {
  240. if($version->id == $this->release)
  241. {
  242. $versionBtn['text'] = $version->version;
  243. $versionBtn['hint'] = $version->version;
  244. }
  245. $versionBtn['dropdown']['items'][] = array(
  246. 'text' => $version->version,
  247. 'hint' => $version->version,
  248. 'url' => $this->buildLink($item, $version->id),
  249. 'active' => $version->id == $this->release
  250. );
  251. }
  252. }
  253. $moreBtn = array();
  254. if(!isset($item->hasAction) || $item->hasAction || in_array($item->type, array('mine', 'view', 'collect', 'createdBy', 'editedBy')))
  255. {
  256. $actions = $this->getOperateItems($item);
  257. if($actions)
  258. {
  259. $moreBtn = array(
  260. 'key' => 'more',
  261. 'icon' => 'ellipsis-v',
  262. 'type' => 'dropdown',
  263. 'caret' => false,
  264. 'dropdown' => array(
  265. 'placement' => 'bottom-end',
  266. 'items' => $actions
  267. )
  268. );
  269. }
  270. }
  271. $actions = array();
  272. if($versionBtn) $actions[] = $versionBtn;
  273. if($moreBtn) $actions[] = $moreBtn;
  274. return $actions ? $actions : null;
  275. }
  276. private function getOperateItems($item)
  277. {
  278. $menus = array();
  279. if(in_array($item->type, array('docLib', 'apiLib')))
  280. {
  281. $itemID = $item->id ? $item->id : $item->parentID;
  282. $moduleName = $item->type == 'docLib' ? 'doc' : 'api';
  283. if(hasPriv($moduleName, 'addCatalog') && !(isset($item->objectType) && $item->objectType == 'custom' && $item->parent == 0))
  284. {
  285. $menus['adddirectory'] = array(
  286. 'key' => 'adddirectory',
  287. 'icon' => 'add-directory',
  288. 'text' => $this->lang->doc->libDropdown['addModule'],
  289. 'onClick' => jsRaw("() => addModule({$itemID}, 'child')")
  290. );
  291. }
  292. if($item->type == 'docLib' && ($item->objectType == 'mine' || ($item->objectType == 'custom' && $item->parent > 0)) && hasPriv($moduleName, 'moveLib'))
  293. {
  294. $menus['movelib'] = array(
  295. 'key' => 'movelib',
  296. 'icon' => 'folder-move',
  297. 'text' => $this->lang->doc->moveTo,
  298. 'data-toggle' => 'modal',
  299. 'data-size' => 'sm',
  300. 'data-url' => createlink($moduleName, 'moveLib', "libID={$itemID}")
  301. );
  302. }
  303. if(hasPriv($moduleName, 'editLib'))
  304. {
  305. $menus['editlib'] = array(
  306. 'key' => 'editlib',
  307. 'icon' => 'edit',
  308. 'text' => (isset($item->objectType) && $item->objectType == 'custom' && $item->parent == 0) ? $this->lang->doc->libDropdown['editSpace'] : $this->lang->doc->libDropdown['editLib'],
  309. 'data-toggle' => 'modal',
  310. 'data-url' => createlink($moduleName, 'editLib', "libID={$itemID}")
  311. );
  312. }
  313. if(hasPriv($moduleName, 'deleteLib'))
  314. {
  315. $menus['dellib'] = array(
  316. 'key' => 'dellib',
  317. 'icon' => 'trash',
  318. 'text' => (isset($item->objectType) && $item->objectType == 'custom' && $item->parent == 0) ? $this->lang->doc->libDropdown['deleteSpace'] : $this->lang->doc->libDropdown['deleteLib'],
  319. 'innerClass' => 'ajax-submit',
  320. 'data-url' => createLink($moduleName, 'deleteLib', "libID={$itemID}"),
  321. 'data-confirm' => (isset($item->objectType) && $item->objectType == 'custom' && $item->parent == 0) ? $this->lang->doc->confirmDeleteSpace : $this->lang->{$moduleName}->confirmDeleteLib
  322. );
  323. }
  324. if($item->objectType == 'mine' && $item->main == 1) unset($menus['dellib'], $menus['movelib']);
  325. }
  326. elseif($item->type == 'module')
  327. {
  328. $moduleName = $item->objectType == 'api' ? 'api' : 'doc';
  329. if(hasPriv($moduleName, 'addCatalog'))
  330. {
  331. $menus['adddirectory'] = array(
  332. 'key' => 'adddirectory',
  333. 'icon' => 'add-directory',
  334. 'text' => $this->lang->doc->libDropdown['addSameModule'],
  335. 'onClick' => jsRaw("() => addModule({$item->id}, 'same')")
  336. );
  337. $menus['addsubdirectory'] = array(
  338. 'key' => 'addsubdirectory',
  339. 'icon' => 'add-directory',
  340. 'text' => $this->lang->doc->libDropdown['addSubModule'],
  341. 'onClick' => jsRaw("() => addModule({$item->id}, 'child')")
  342. );
  343. }
  344. if(hasPriv($moduleName, 'editCatalog'))
  345. {
  346. $menus['editmodule'] = array(
  347. 'key' => 'editmodule',
  348. 'icon' => 'edit',
  349. 'text' => $this->lang->doc->libDropdown['editModule'],
  350. 'link' => '',
  351. 'data-toggle' => 'modal',
  352. 'data-url' => createlink($moduleName, 'editCatalog', "moduleID={$item->id}&type=" . ($this->rawModule == 'api' ? 'api' : 'doc'))
  353. );
  354. }
  355. if(hasPriv($moduleName, 'deleteCatalog'))
  356. {
  357. $menus['delmodule'] = array(
  358. 'key' => 'delmodule',
  359. 'icon' => 'trash',
  360. 'text' => $this->lang->doc->libDropdown['delModule'],
  361. 'innerClass' => 'ajax-submit',
  362. 'data-url' => createLink($moduleName, 'deleteCatalog', "moduleID={$item->id}"),
  363. 'data-confirm' => $this->lang->doc->confirmDeleteModule
  364. );
  365. }
  366. }
  367. return array_values($menus);
  368. }
  369. private function getIcon($item)
  370. {
  371. if(isset($item->objectType) && $item->objectType == 'custom' && $item->parent == 0) return ''; // 团队空间下的空间不显示图标
  372. $type = $item->type;
  373. if($type == 'apiLib') return 'interface-lib';
  374. if($type == 'docLib') return 'wiki-lib';
  375. if($type == 'annex') return 'annex-lib';
  376. if($type == 'execution') return 'execution';
  377. if($type == 'text') return 'file-text';
  378. if($type == 'word') return 'file-word';
  379. if($type == 'ppt') return 'file-powerpoint';
  380. if($type == 'excel') return 'file-excel';
  381. return '';
  382. }
  383. private function getTitle()
  384. {
  385. global $lang;
  386. $activeKey = $this->prop('activeKey');
  387. if(empty($activeKey)) return $this->prop('title');
  388. foreach($this->modules as $module)
  389. {
  390. if($module->id == $activeKey) return $module->name;
  391. }
  392. return '';
  393. }
  394. private function buildBtns()
  395. {
  396. $settingLink = $this->prop('settingLink');
  397. $settingText = $this->prop('settingText');
  398. if(!$settingLink) return null;
  399. global $app;
  400. $lang = $app->loadLang('datatable')->datatable;
  401. if(!$settingText) $settingText = $lang->moduleSetting;
  402. return div
  403. (
  404. setClass('col gap-2 py-3 px-7'),
  405. $settingLink ? a
  406. (
  407. setClass('btn'),
  408. setStyle('background', 'rgb(var(--color-primary-50-rgb))'),
  409. setStyle('box-shadow', 'none'),
  410. set('data-app', $app->tab),
  411. set('data-size', array('width' => '600px', 'height' => '210px')),
  412. set('data-toggle', 'modal'),
  413. set('data-class-name', 'doc-setting-modal'),
  414. set::href($settingLink),
  415. span
  416. (
  417. setClass('text-primary'),
  418. $settingText
  419. )
  420. ) : null
  421. );
  422. }
  423. protected function build()
  424. {
  425. global $app;
  426. $this->setMenuTreeProps();
  427. $title = $this->getTitle();
  428. $menuLink = $this->prop('menuLink', '');
  429. $objectID = $this->prop('objectID', 0);
  430. $treeProps = $this->props->pick(array('items', 'activeClass', 'activeIcon', 'activeKey', 'onClickItem', 'defaultNestedShow', 'changeActiveKey', 'isDropdownMenu', 'hover', 'sortable', 'onSort', 'canSortTo'));
  431. $preserve = $this->prop('preserve', $app->rawModule . '-' . $app->rawMethod);
  432. $isInSidebar = $this->parent instanceof sidebar;
  433. $header = h::header
  434. (
  435. setClass('doc-menu-header h-10 flex items-center pl-4 flex-none gap-3', $isInSidebar ? 'is-fixed rounded rounded-r-none canvas' : ''),
  436. span
  437. (
  438. setClass('module-title text-lg font-semibold clip'),
  439. $title
  440. )
  441. );
  442. $treeType = (!empty($treeProps['onSort']) || !empty($treeProps['sortable'])) ? 'sortableTree' : 'tree';
  443. return array
  444. (
  445. $isInSidebar && !$menuLink ? $header : null,
  446. div
  447. (
  448. $menuLink ? dropmenu
  449. (
  450. set::id('docDropmenu'),
  451. set::menuID('docDropmenuMenu'),
  452. set::objectID($objectID),
  453. set::text($title),
  454. set::url($menuLink)
  455. ) : null,
  456. div
  457. (
  458. setClass('doc-menu rounded shadow ring bg-white col'),
  459. h::main
  460. (
  461. setClass($menuLink ? 'pt-3' : ''),
  462. setClass('col flex-auto overflow-y-auto overflow-x-hidden pl-2 pr-1 py-1 scrollbar-hover'),
  463. setStyle('--menu-selected-bg', 'none'),
  464. zui::$treeType
  465. (
  466. set::_tag('menu'),
  467. set::lines(),
  468. set::preserve($preserve),
  469. set::selectedKey(implode(':', $this->selectedKey)),
  470. set::afterRender(jsRaw('function(isFirst){return isFirst && this.toggle(this.props.selectedKey)}')),
  471. set($treeProps)
  472. )
  473. ),
  474. $this->buildBtns()
  475. ),
  476. $isInSidebar ? h::js("$('#mainContainer').addClass('has-doc-menu-header')") : null
  477. )
  478. );
  479. }
  480. }