MenuWidget.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. <template>
  2. <div class="menu-side left_col scroll-view" :style="{ width: width + 'px' }">
  3. <div class="navbar nav_title" style="border: 0">
  4. <a href="#/desktop/dashboard" class="site_title"><img style="width: var(--menu-logo-width)" :src="logoUrl" /></a>
  5. </div>
  6. <div class="clearfix" />
  7. <div id="sidebar-menu" class="main_menu_side hidden-print main_menu">
  8. <div class="menu_section">
  9. <div class="menu-div">
  10. <input
  11. v-model="searchText" autocomplete="off" type="text" class="form-control menu-search"
  12. :placeholder="$t('lang.menuWidget.searchMenu')"
  13. style="background-color: var(--menu-search-input-background-color)"
  14. />
  15. </div>
  16. <ul class="nav menu-nav side-menu">
  17. <li>
  18. <a href="#/desktop/dashboard">
  19. <i class="fa fa-home" /> <span>首页</span>
  20. </a>
  21. </li>
  22. <template v-for="rootNode in rootNodes">
  23. <li
  24. v-if="rootNode.show" :key="rootNode.uuid" :name="rootNode.no" :model="rootNode"
  25. :class="{ active: selectedNode === rootNode }"
  26. >
  27. <a @click="selectMenuNode(rootNode)">
  28. <i :class="rootNode.icon" />
  29. {{ Language.getMenuNameTrl($i18n.locale, rootNode) }}
  30. <span class="fa fa-chevron-down" />
  31. </a>
  32. <ul class="nav menu-nav child_menu" :class="{ active: rootNode.expand == true }">
  33. <template v-for="subMenuItem in rootNode.subMenus" :key="subMenuItem.uuid">
  34. <MenuNode :model="subMenuItem" @select-node="selectNode($event, rootNode.subMenus)" />
  35. </template>
  36. </ul>
  37. </li>
  38. </template>
  39. </ul>
  40. </div>
  41. </div>
  42. </div>
  43. </template>
  44. <script>
  45. import Common from '../common/Common.js';
  46. import Language from '../common/Language.js';
  47. import MenuNode from './MenuNode.vue';
  48. import { Uuid } from 'pc-component-v3';
  49. import { useRoleStateSingleton } from './RoleState.js';
  50. export default {
  51. components: {
  52. MenuNode,
  53. },
  54. props: {
  55. width: {
  56. default: 230,
  57. type: Number,
  58. },
  59. },
  60. data: function () {
  61. this.Language = Language;
  62. return {
  63. rootNodes: [],
  64. selectedNode: {}, // 选择的节点
  65. searchText: '',
  66. logoUrl: '', // logo路径
  67. roleStateInstance: useRoleStateSingleton,
  68. isRefresh: false,
  69. };
  70. },
  71. watch: {
  72. searchText: function (newVal, oldVal) {
  73. this.search(newVal);
  74. },
  75. 'roleStateInstance.userRoleOrTemplate': function (newValue, oldVal) {
  76. this.loadMenuData();
  77. console.log(newValue, oldVal);
  78. if (newValue && oldVal && newValue.roleOrTemplateName !== oldVal.roleOrTemplateName) {
  79. this.$router.push('/desktop/dashboard');
  80. }
  81. },
  82. },
  83. mounted: function () {
  84. var _self = this;
  85. // toggled by jack 20241107
  86. // watch 'roleStateInstance.userRoleOrTemplate' will trigger loadMenuData() method
  87. // this.loadMenuData();
  88. this.getLogoName();
  89. },
  90. methods: {
  91. /// 加载数据
  92. loadMenuData: function () {
  93. console.log('loadMenuData');
  94. var _self = this;
  95. _self.rootNodes = [];
  96. $.ajax({
  97. url: Common.getApiURL('MenuResourceV2/listCanVist'),
  98. type: 'POST',
  99. data: _self.roleStateInstance.userRoleOrTemplate == null ? null : JSON.stringify(_self.roleStateInstance.userRoleOrTemplate),
  100. dataType: 'json',
  101. contentType: 'application/json',
  102. beforeSend: function (request) {
  103. Common.addTokenToRequest(request);
  104. },
  105. success: function (data) {
  106. $.ajax({
  107. url: Common.getApiURL('UserMenuResource/queryAddMenus'),
  108. type: 'get',
  109. dataType: 'json',
  110. beforeSend: function (request) {
  111. Common.addTokenToRequest(request);
  112. },
  113. success: function (data1) {
  114. if (data1 != null) {
  115. console.log(data1);
  116. _self.formateCollectionMenuData(data, data1);
  117. } else {
  118. _self.formateMenuData(data);
  119. }
  120. },
  121. error: function (XMLHttpRequest, textStatus, errorThrown) {
  122. Common.processException(XMLHttpRequest, textStatus, errorThrown);
  123. },
  124. });
  125. },
  126. error: function (XMLHttpRequest, textStatus, errorThrown) {
  127. Common.processException(XMLHttpRequest, textStatus, errorThrown);
  128. },
  129. });
  130. },
  131. // 格式化菜单数据
  132. formateMenuData: function (datas) {
  133. var _self = this;
  134. if (datas == null || datas.length == 0) {
  135. return;
  136. }
  137. // 对菜单进行排序和设置expand
  138. function initNode(nodes) {
  139. if (nodes instanceof Array) {
  140. nodes.forEach(function (node) {
  141. node.expand = false;
  142. node.show = true;
  143. node.uuid = Uuid.createUUID();
  144. if (node.subMenus instanceof Array) {
  145. initNode(node.subMenus);
  146. }
  147. });
  148. }
  149. }
  150. initNode(datas);
  151. this.rootNodes = datas;
  152. _self.$nextTick(function () {
  153. _self.initView();
  154. });
  155. },
  156. // 格式化菜单数据
  157. formateCollectionMenuData: function (datas, collections) {
  158. var _self = this;
  159. if (
  160. (datas == null || datas.length == 0) &&
  161. (collections == null || collections.length == 0)
  162. ) {
  163. return;
  164. }
  165. // 对菜单进行排序和设置expand
  166. function initNode(nodes, collectionDatas) {
  167. if (nodes instanceof Array) {
  168. nodes.forEach(function (node) {
  169. node.expand = false;
  170. node.show = true;
  171. node.uuid = Uuid.createUUID();
  172. collectionDatas.forEach(collectionData => {
  173. if (node.no == collectionData.no) {
  174. node.favorite = true;
  175. }
  176. });
  177. if (node.subMenus instanceof Array) {
  178. initNode(node.subMenus, collectionDatas);
  179. }
  180. });
  181. }
  182. }
  183. initNode(datas, collections);
  184. this.rootNodes = datas;
  185. _self.$nextTick(function () {
  186. _self.initView();
  187. });
  188. },
  189. setContentHeight: function () {
  190. var $RIGHT_COL = $('.right_col');
  191. var $BODY = $('body');
  192. var $LEFT_COL = $('.left_col');
  193. var $NAV_MENU = $('.nav_menu');
  194. var bodyHeight = $(window).height();
  195. var leftColHeight = $LEFT_COL.eq(1).height();
  196. var contentHeight =
  197. (bodyHeight < leftColHeight ? leftColHeight : bodyHeight) -
  198. ($NAV_MENU.height() + 2);
  199. $RIGHT_COL.css('min-height', contentHeight);
  200. },
  201. // 初始化界面
  202. initView: function () {
  203. var _self = this;
  204. // 窗口大小改变的时候,重新计算高度
  205. $(window).resize(function () {
  206. _self.setContentHeight();
  207. });
  208. _self.setContentHeight();
  209. },
  210. // 选择菜单节点
  211. selectMenuNode: function (rootNode) {
  212. if (rootNode.expand) {
  213. rootNode.expand = false;
  214. } else {
  215. rootNode.expand = true;
  216. }
  217. },
  218. selectNode: function (node, subMenus) {
  219. if (node.expand) {
  220. subMenus.forEach(function (item) {
  221. if (item != node) {
  222. //这里控制菜单是否可以同时展开
  223. // item.expand = false;
  224. }
  225. });
  226. }
  227. },
  228. /**
  229. * 搜索菜单
  230. */
  231. search: function (inputText) {
  232. //判断一个节点及其子节点是否含有搜索的内容
  233. function filterNode(node) {
  234. var name = node.name == null ? "" : node.name;
  235. var nameEng = node.nameEng == null ? "" : node.nameEng;
  236. if (name.indexOf(inputText) != -1 ||
  237. name.toLowerCase().indexOf(inputText.toLowerCase()) != -1 ||
  238. nameEng.indexOf(inputText) != -1 ||
  239. nameEng.toLowerCase().indexOf(inputText.toLowerCase()) != -1
  240. ) {
  241. node.show = true;
  242. } else {
  243. node.show = false;
  244. }
  245. if (node.subMenus != undefined && node.subMenus instanceof Array) {
  246. node.subMenus.forEach(function (node2) {
  247. filterNode(node2);
  248. });
  249. }
  250. }
  251. /**
  252. * 递归修改属性为可见的节点的父节点为可见
  253. * @param {Object} node
  254. */
  255. function parseParent(node, level) {
  256. // 防止循环数据
  257. if (level > 5) {
  258. return;
  259. }
  260. if (node.subMenus != undefined && node.subMenus instanceof Array) {
  261. node.subMenus.forEach(function (node1) {
  262. parseParent(node1, level + 1);
  263. //递归结束时,设置可见节点的父节点为可见
  264. if (node1.show == true) {
  265. node.show = true;
  266. node.expand = true;
  267. node1.expand = true;
  268. }
  269. });
  270. }
  271. }
  272. var _self = this;
  273. _self.setNodeView();
  274. if (inputText) {
  275. //遍历所有根节点与传销内容无关的设置不可见
  276. this.rootNodes.forEach(function (node) {
  277. filterNode(node);
  278. });
  279. this.rootNodes.forEach(function (node) {
  280. parseParent(node, 0);
  281. });
  282. }
  283. },
  284. /**
  285. * 设置全部菜单可见
  286. */
  287. setNodeView: function () {
  288. var _self = this;
  289. function initNode(nodes) {
  290. if (nodes instanceof Array) {
  291. nodes.sort(function (a, b) {
  292. if (a.sortNo != undefined && b.sortNo != undefined) {
  293. return a.sortNo - b.sortNo;
  294. } else {
  295. return 0;
  296. }
  297. });
  298. nodes.forEach(function (node) {
  299. node.expand = false;
  300. node.show = true;
  301. if (node.subMenus instanceof Array) {
  302. initNode(node.subMenus);
  303. }
  304. });
  305. }
  306. }
  307. initNode(_self.rootNodes);
  308. },
  309. // 查询系统logo
  310. getLogoName: function () {
  311. const _self = this;
  312. $.ajax({
  313. type: 'GET',
  314. url: Common.getApiURL('clientLogoResource/selectByClientId?logoName=menuLogoName'),
  315. dataType: 'json',
  316. beforeSend: function (request) {
  317. Common.addTokenToRequest(request);
  318. },
  319. success: function (res) {
  320. if (res.errorCode === 0) {
  321. _self.logoUrl = `/Files/0/Images/com.leanwo.prodog.system.model.ClientLogo/${res.data.imageName}`;
  322. }
  323. },
  324. error: function (XMLHttpRequest, textStatus, errorThrown) {
  325. Common.processException(XMLHttpRequest, textStatus, errorThrown);
  326. },
  327. });
  328. },
  329. },
  330. };
  331. </script>
  332. <style scoped>
  333. .menu-div {
  334. padding-left: 10px;
  335. padding-right: 10px;
  336. }
  337. .call-div {
  338. color: white;
  339. padding-top: 10px;
  340. padding-left: 15px;
  341. padding-right: 10px;
  342. font-size: medium;
  343. cursor: pointer;
  344. }
  345. </style>
  346. <style>
  347. .menu-side {
  348. background-color: var(--menu-side-background-color) !important;
  349. position: fixed;
  350. top: 0px;
  351. bottom: 0;
  352. left: 0;
  353. /* padding-top: 51px; */
  354. width: 230px;
  355. overflow-y: auto;
  356. overflow-x: hidden;
  357. }
  358. .main_menu_side {
  359. padding: 0;
  360. }
  361. .main_menu .fa {
  362. width: 26px;
  363. opacity: 0.99;
  364. display: inline-block;
  365. font-family: FontAwesome;
  366. font-style: normal;
  367. font-weight: normal;
  368. font-size: 18px;
  369. -webkit-font-smoothing: antialiased;
  370. -moz-osx-font-smoothing: grayscale;
  371. }
  372. .main_menu .navbar-nav>li>a {
  373. color: #fff !important;
  374. }
  375. .main_menu .nav.side-menu>li {
  376. position: relative;
  377. display: block;
  378. cursor: pointer;
  379. }
  380. .main_menu .nav.side-menu>li>a {
  381. margin-bottom: 6px;
  382. }
  383. .main_menu .nav.side-menu>li>a:hover {
  384. color: #f2f5f7 !important;
  385. }
  386. .main_menu .nav.side-menu>li>a,
  387. .main_menu .nav.child_menu>li>a {
  388. /* color: #e7e7e7; */
  389. color: #fff;
  390. font-weight: 500;
  391. }
  392. .main_menu .nav.side-menu>li.current-page,
  393. .main_menu .nav.side-menu>li.active {
  394. border-right: 5px solid #1abb9c;
  395. }
  396. .main_menu .nav.side-menu>li.active>a {
  397. text-shadow: rgba(0, 0, 0, 0.25) 0 -1px 0;
  398. background: linear-gradient(#334556, #2c4257), #2a3f54;
  399. box-shadow: rgba(0, 0, 0, 0.25) 0 1px 0,
  400. inset rgba(255, 255, 255, 0.16) 0 1px 0;
  401. }
  402. .main_menu .nav.child_menu li li a:hover {
  403. background-color: var(--menu-select-color);
  404. }
  405. .main_menu .nav.child_menu {
  406. display: none;
  407. }
  408. .main_menu .nav.child_menu.active {
  409. display: block;
  410. }
  411. /*.nav.child_menu li:hover,
  412. .nav.child_menu li.active {
  413. background-color: #495d70;
  414. }
  415. .nav.child_menu li a:hover {
  416. background-color: #495d70;
  417. }*/
  418. .main_menu .nav.child_menu li {
  419. padding-left: 36px;
  420. }
  421. .main_menu .nav-md ul.nav.child_menu li:before {
  422. background: #425668;
  423. bottom: auto;
  424. content: "";
  425. height: 8px;
  426. left: 23px;
  427. margin-top: 15px;
  428. position: absolute;
  429. right: auto;
  430. width: 8px;
  431. z-index: 1;
  432. border-radius: 50%;
  433. }
  434. .main_menu .nav-md ul.nav.child_menu li:after {
  435. border-left: 1px solid #425668;
  436. bottom: 0;
  437. content: "";
  438. left: 27px;
  439. position: absolute;
  440. top: 0;
  441. }
  442. .main_menu .nav.child_menu li li:hover,
  443. .main_menu .nav.child_menu li li.active {
  444. background-color: var(--menu-select-color);
  445. }
  446. .main_menu .nav.child_menu li li a:hover,
  447. .main_menu .nav.child_menu li li a.active {
  448. color: #fff;
  449. }
  450. .main_menu .nav>li>a {
  451. position: relative;
  452. display: block;
  453. padding: 13px 15px 12px;
  454. }
  455. /*.nav li.current-page {
  456. background: rgba(255, 255, 255, 0.05);
  457. }
  458. .nav li li li.current-page {
  459. background: none;
  460. }*/
  461. .main_menu .nav li li.current-page a {
  462. color: #fff;
  463. }
  464. .main_menu .nav.navbar-nav>li>a {
  465. color: #515356 !important;
  466. }
  467. .main_menu .nav.top_menu>li>a {
  468. position: relative;
  469. display: block;
  470. padding: 10px 15px;
  471. color: #34495e !important;
  472. }
  473. /* .nav > li > a:hover, .nav > li > a:focus {
  474. background-color: transparent;
  475. }*/
  476. .main_menu .menu-nav>li>a:focus,
  477. .main_menu .menu-nav>li>a:hover {
  478. text-decoration: none;
  479. background-color: transparent !important;
  480. }
  481. .main_menu .menu-search {
  482. /* background-color: #1c3e4b; */
  483. color: #fff;
  484. }
  485. </style>