@utogether/udp-core 2.0.0-beta.1 → 2.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/dist/{403-CTJDBjz7.js → 403-CxOqlq0f.js} +3 -3
  2. package/dist/403-WCboRvzZ-ZAPgYStl.js +65 -0
  3. package/dist/404-2V0Qi-d7-DkNwQggp.js +65 -0
  4. package/dist/{404-BFYkWIkQ.js → 404-xdB6lFeQ.js} +1 -1
  5. package/dist/500-BYfzvixf-CGpEieyQ.js +67 -0
  6. package/dist/{500-DGG3qadg.js → 500-C94bRK2S.js} +2 -2
  7. package/dist/AuthorityInfo-DBovfUjB-DpQwT9Q5.js +4 -0
  8. package/dist/{AuthorityInfo-ozZIo1Te.js → AuthorityInfo-Dy3b_nFN.js} +1 -1
  9. package/dist/{AuthorityInfo.vue_vue_type_style_index_0_lang-DujerENw.js → AuthorityInfo.vue_vue_type_style_index_0_lang-Bh7IbcdY.js} +2 -2
  10. package/dist/AuthorityInfo.vue_vue_type_style_index_0_lang-D-l_Az3s-Cw7WmFxz.js +100 -0
  11. package/dist/AuthorityPanel-CRlAwbaI-G7pZXKdE.js +4 -0
  12. package/dist/AuthorityPanel.vue_vue_type_style_index_0_lang-DxhZjp1S-CJvk3pW8.js +114 -0
  13. package/dist/Company-B4vsXy2I-CkNCOqjJ.js +25 -0
  14. package/dist/{Company-6VJtwh23.js → Company-gKkfnhLt.js} +3 -3
  15. package/dist/{CompanyPanel-B2P488mq.js → CompanyPanel-BF5Pc35s.js} +7 -7
  16. package/dist/CompanyPanel-Czcx8Gyw-CvndXwB_.js +206 -0
  17. package/dist/DataSet-DT-rGICv-DaUfgbxk.js +147 -0
  18. package/dist/{Department-BnwoLEOC.js → Department-D0I3QVZe.js} +3 -3
  19. package/dist/Department-D0dVUrGC-C6HJcYI1.js +25 -0
  20. package/dist/{DepartmentPanel-CRrrmxtv.js → DepartmentPanel-2LHODgc7.js} +22 -22
  21. package/dist/DepartmentPanel-e91Lxr1j-BS_A-ILd.js +254 -0
  22. package/dist/{DesignPanel-CvbccgX2.js → DesignPanel-CtF3cAAQ.js} +1 -1
  23. package/dist/DesignPanel-DdFl_ohi-uCwFxcl1.js +4 -0
  24. package/dist/DesignPanel.vue_vue_type_style_index_0_lang-BACPrfUI-cC7yL0uY.js +1013 -0
  25. package/dist/{DesignPanel.vue_vue_type_style_index_0_lang-Dz2tUszs.js → DesignPanel.vue_vue_type_style_index_0_lang-BCYgwoVt.js} +3 -3
  26. package/dist/DictView-BzQLOf_P-DDicwdmM.js +111 -0
  27. package/dist/{DictView-Ce1LoVHh.js → DictView-H3V5hxg3.js} +1 -1
  28. package/dist/{InvOrganization-BwCFZO1X.js → InvOrganization-BiGLnbqe.js} +2 -2
  29. package/dist/InvOrganization-atbhw0CI-BABbVt9V.js +74 -0
  30. package/dist/Org-BW1YHG-Q-Cow7JWlD.js +39 -0
  31. package/dist/{Org-0hzs6b0R.js → Org-DQTCQHNY.js} +2 -2
  32. package/dist/Preview-DJtVsoq1-CCGidQjJ.js +48 -0
  33. package/dist/{Preview--DnEAhwh.js → Preview-DXaiis29.js} +1 -1
  34. package/dist/{ReportDefine-Cu983bTN.js → ReportDefine-BLh4CiER.js} +1 -1
  35. package/dist/ReportDefine-CNx_ob99-6NQAHK-Q.js +10 -0
  36. package/dist/ReportDesign-FbQ6yTJS-C1pPAUSp.js +165 -0
  37. package/dist/{ReportDesign-QsWXXgvo.js → ReportDesign-Ni1YxrLC.js} +7 -7
  38. package/dist/ReportQuery-C5gz8Lgd-Eu2cQmda.js +75 -0
  39. package/dist/{ReportQuery-BjKIIhPu.js → ReportQuery-C8G88_qF.js} +1 -1
  40. package/dist/ReportQueryFrom-C7scua5v-uHXWq8Gy.js +4 -0
  41. package/dist/{ReportQueryFrom-DIjBO6Fx.js → ReportQueryFrom-CuZKPtB4.js} +1 -1
  42. package/dist/{ReportQueryFrom.vue_vue_type_style_index_0_lang-0T62cUMK.js → ReportQueryFrom.vue_vue_type_style_index_0_lang-BAn_siFW.js} +7 -7
  43. package/dist/ReportQueryFrom.vue_vue_type_style_index_0_lang-DCbz67Wa-vK6r2uCl.js +178 -0
  44. package/dist/ReportTemplate-BDANdIWv-D_IGjR1w.js +161 -0
  45. package/dist/{ReportTemplate-2uk9tJcy.js → ReportTemplate-DFnDXWmx.js} +8 -8
  46. package/dist/{Role-_QP8QEaI.js → Role-BQ7hsfPn.js} +3 -3
  47. package/dist/Role-ByB0WbxW-5E8Mb148.js +25 -0
  48. package/dist/RoleAssign-DW6iC_0v-Bis61auk.js +26 -0
  49. package/dist/{RoleAssign-C2gkcmEQ.js → RoleAssign-UPCg2d4G.js} +3 -3
  50. package/dist/RolePanel-BM2MaQPU-CgcsbfaX.js +4 -0
  51. package/dist/{RolePanel-vnl_lXNY.js → RolePanel-C-mQ5XRq.js} +1 -1
  52. package/dist/RolePanel-ClQy8DBL-DARL4O-o.js +4 -0
  53. package/dist/{RolePanel-B8FPNGaA.js → RolePanel-CoSXOCZN.js} +1 -1
  54. package/dist/RolePanel.vue_vue_type_script_setup_true_lang-Bc_Ao_PU-DMmxr2iI.js +132 -0
  55. package/dist/{RolePanel.vue_vue_type_script_setup_true_lang-bxHXrBYl.js → RolePanel.vue_vue_type_script_setup_true_lang-Bg-t2UhF.js} +7 -7
  56. package/dist/{RolePanel.vue_vue_type_script_setup_true_lang-BuQbYEEI.js → RolePanel.vue_vue_type_script_setup_true_lang-D-o7HYZ8.js} +8 -8
  57. package/dist/RolePanel.vue_vue_type_script_setup_true_lang-fthF1zkp-JsbQJoDy.js +154 -0
  58. package/dist/ScrollPanel.vue_vue_type_style_index_0_lang-ByWIwajm-BsBf2Vs3.js +100 -0
  59. package/dist/{ScrollPanel.vue_vue_type_style_index_0_lang-C6yZXBqB.js → ScrollPanel.vue_vue_type_style_index_0_lang-CCsoxDfk.js} +1 -1
  60. package/dist/Staff-CuxzvhD9-D0KTcyRh.js +25 -0
  61. package/dist/{Staff-CHWrMIEb.js → Staff-D-CmvG1R.js} +3 -3
  62. package/dist/StaffInfo-CY7gUICu-EZUhUwIp.js +4 -0
  63. package/dist/{StaffInfo-Cpq2eayz.js → StaffInfo-Idhvuc6e.js} +1 -1
  64. package/dist/StaffInfo.vue_vue_type_script_setup_true_lang-Dd8gtQz8-Dlkt-eCn.js +108 -0
  65. package/dist/{StaffInfo.vue_vue_type_script_setup_true_lang-BVCjF2i1.js → StaffInfo.vue_vue_type_script_setup_true_lang-MUGKjnHU.js} +1 -1
  66. package/dist/{StaffPanel-D2BMXt5p.js → StaffPanel-CDDmAYE5.js} +1 -1
  67. package/dist/StaffPanel-CwqG0_xr-CrvzLJoc.js +4 -0
  68. package/dist/{StaffPanel.vue_vue_type_script_setup_true_lang-B6jAGo-g.js → StaffPanel.vue_vue_type_script_setup_true_lang-Bdilqbee.js} +42 -42
  69. package/dist/StaffPanel.vue_vue_type_script_setup_true_lang-DCdBy8Hu-BXrueqi0.js +143 -0
  70. package/dist/SysUser-9Q2UJWhi-DprWXFlR.js +15 -0
  71. package/dist/{SysUser-om6H1BeC.js → SysUser-a-j5bppr.js} +2 -2
  72. package/dist/{SysUserPanel-CH0HPP7h.js → SysUserPanel-6V232wwB.js} +1 -1
  73. package/dist/SysUserPanel-bJy69O7x-BJtBgFEs.js +4 -0
  74. package/dist/{SysUserPanel.vue_vue_type_script_setup_true_lang-Dpl13ee0.js → SysUserPanel.vue_vue_type_script_setup_true_lang-BOtHuMVy.js} +2 -2
  75. package/dist/SysUserPanel.vue_vue_type_script_setup_true_lang-DUPFqgz3-BSqRCMen.js +356 -0
  76. package/dist/{SystemMenu-BkG_DKxA.js → SystemMenu-BnVeOSOQ.js} +7 -7
  77. package/dist/SystemMenu-DGOAolc1-CJSLHP8i.js +156 -0
  78. package/dist/UserInfo-CKoOHkAM-DDWp0I_U.js +4 -0
  79. package/dist/{UserInfo-D9croxUe.js → UserInfo-Cax9b2nw.js} +1 -1
  80. package/dist/UserInfo.vue_vue_type_style_index_0_lang-BaT53SSu-DsNaAomO.js +160 -0
  81. package/dist/{UserInfo.vue_vue_type_style_index_0_lang-DLIP8xpN.js → UserInfo.vue_vue_type_style_index_0_lang-D-cw11i9.js} +2 -2
  82. package/dist/await-to-js.es5-Bv3Eu4mi-UCggJjes.js +10 -0
  83. package/dist/await-to-js.es5-Bv3Eu4mi.js +10 -0
  84. package/dist/childView-0YZQ6GBn-CEqg7k5d.js +4 -0
  85. package/dist/{childView-BY_Ip-l1.js → childView-DKG3eZo8.js} +1 -1
  86. package/dist/childView-DKkgi3yo-D47ft-vi.js +4 -0
  87. package/dist/{childView-CHp_TueS.js → childView-siumuBz0.js} +1 -1
  88. package/dist/childView.vue_vue_type_style_index_0_lang-ABMrGInv-ClOdE_sa.js +143 -0
  89. package/dist/{childView.vue_vue_type_style_index_0_lang-BNHbDRjt.js → childView.vue_vue_type_style_index_0_lang-CUfXDS1q.js} +8 -8
  90. package/dist/{childView.vue_vue_type_style_index_0_lang-CbjGf7Z7.js → childView.vue_vue_type_style_index_0_lang-CW3EbAmW.js} +8 -8
  91. package/dist/childView.vue_vue_type_style_index_0_lang-CaW106ve-CClTM8PK.js +180 -0
  92. package/dist/{code-rule-Ce6yWqCq.js → code-rule-Dtoree6F.js} +2 -2
  93. package/dist/code-rule-wQyfgpNL-C4evD4Co.js +148 -0
  94. package/dist/core.es.js +10 -10
  95. package/dist/cron-task-C-kryDtd-C0NuGZCe.js +135 -0
  96. package/dist/{cron-task-DziaH0rI.js → cron-task-GSRgA7S5.js} +2 -2
  97. package/dist/flow-task-B07st2aD-CgQvagSQ.js +10 -0
  98. package/dist/frameView-BOGA3ezf-QoSxzmSQ.js +44 -0
  99. package/dist/{frameView-DNeCVQaY.js → frameView-Cy6wxW0K.js} +1 -1
  100. package/dist/{index-BabfUVv_.js → index-Dc7xWMiC.js} +1057 -1033
  101. package/dist/layout-home-3Dy4onl4.js +228 -0
  102. package/dist/layout-home-CYHksXN_-Basy-3IH.js +228 -0
  103. package/dist/layoutView-BaRvAbIa-xVgfqspc.js +3302 -0
  104. package/dist/{layoutView-12Mlp9A2.js → layoutView-DMjNscJ-.js} +8 -8
  105. package/dist/log-in-Cx1dGik8-BVeEHeZ8.js +117 -0
  106. package/dist/{log-in-1NGaA5OM.js → log-in-VAG6Cvcx.js} +8 -8
  107. package/dist/log-out-COYdxrNC-ftopGZdE.js +130 -0
  108. package/dist/{log-out-B8_atGcQ.js → log-out-DZGaMCjC.js} +4 -4
  109. package/dist/{login-5dydO6GR.js → login-CqVMdNHs.js} +16 -16
  110. package/dist/login-Dg9ofNS8-Bocp1XMA.js +241 -0
  111. package/dist/login-log-DJBGJVV0-AoOyj0jD.js +70 -0
  112. package/dist/lov-view-C0T5prk8-B2DBmn55.js +97 -0
  113. package/dist/{lov-view-DPvGUu3h.js → lov-view-Cc68_28B.js} +7 -7
  114. package/dist/{menuInfo-DVADYfEK.js → menuInfo-BIrIaJlH.js} +1 -1
  115. package/dist/menuInfo-BZJ_q7bz-Drho-_QC.js +4 -0
  116. package/dist/menuInfo.vue_vue_type_style_index_0_lang-BA8xjUo3-BCZ1wipf.js +363 -0
  117. package/dist/{menuInfo.vue_vue_type_style_index_0_lang-DgsifDrm.js → menuInfo.vue_vue_type_style_index_0_lang-DlE4w35X.js} +2 -2
  118. package/dist/{pda-app-BZXs-2BQ.js → pda-app-DnjphrRS.js} +9 -9
  119. package/dist/pda-app-m9hsppHo-B4xyja1o.js +710 -0
  120. package/dist/redirect-BqegffKC-CBCIuqmz.js +15 -0
  121. package/dist/resource-C6KEIXu--B_ddyecm.js +97 -0
  122. package/dist/{resource-BZA9NFKc.js → resource-DISgPDM7.js} +4 -4
  123. package/dist/su-welcome-BjbuSrBZ.js +49424 -0
  124. package/dist/su-welcome-CYYy-dzr-D3RKPoB0.js +42089 -0
  125. package/dist/sys-config-DCjJGtht-DARDjlrt.js +370 -0
  126. package/dist/{sys-config-DQmNjWH4.js → sys-config-Yc9vh1t1.js} +13 -13
  127. package/dist/udp-core.css +1 -1
  128. package/dist/utogether-MlnyYtNS-CGgjFNPS.js +4 -0
  129. package/package.json +5 -2
  130. package/src/App.vue +71 -71
  131. package/src/components/udp/content/index.vue +88 -88
  132. package/src/components/udp/form-upload/form-upload.vue +492 -492
  133. package/src/components/udp/grid/index.vue +524 -524
  134. package/src/components/udp/index.ts +6 -6
  135. package/src/components/udp/ut-stamp-badge/index.vue +271 -271
  136. package/src/components/udp/utils.ts +408 -408
  137. package/src/layout/components/lay-content/index.vue +136 -136
  138. package/src/layout/components/lay-search/components/SearchModal.vue +181 -189
  139. package/src/layout/components/lay-setting/index.vue +503 -503
  140. package/src/layout/components/lay-sidebar/sidebar-logo.vue +101 -101
  141. package/src/layout/components/lay-tag/index.vue +598 -598
  142. package/src/layout/hooks/useNav.ts +176 -176
  143. package/src/layout/hooks/useTag.ts +227 -227
  144. package/src/layout/layoutView.vue +216 -216
  145. package/src/layout/types.ts +93 -93
  146. package/src/main.ts +111 -112
  147. package/src/plugins/i18n/zh.ts +1 -0
  148. package/src/plugins/vxe-table/index.ts +116 -116
  149. package/src/plugins/vxe-table/render.tsx +968 -968
  150. package/src/router/index.ts +187 -187
  151. package/src/router/modules/home.ts +32 -32
  152. package/src/router/utils.ts +420 -420
  153. package/src/store/modules/epTheme.ts +48 -48
  154. package/src/style/vxetable.scss +364 -356
  155. package/src/utils/dataFormat/index.ts +222 -222
  156. package/src/utils/lifecycle.ts +39 -39
  157. package/src/views/organization/department/DepartmentPanel.vue +303 -303
  158. package/src/views/organization/staff/StaffInfo.vue +127 -127
  159. package/src/views/organization/staff/StaffPanel.vue +3 -3
  160. package/src/views/system/layout/layout-home.vue +45 -7
  161. package/src/views/system/menu/AuthorityPanel.vue +141 -141
  162. package/src/views/system/menu/SystemMenu.vue +194 -194
  163. package/src/views/system/menu/menuInfo.vue +1 -1
  164. package/src/views/system/sysUser/SysUserPanel.vue +363 -363
  165. package/src/views/udev/coderule/code-rule.vue +132 -132
  166. package/src/views/udev/dict/DictView.vue +118 -118
  167. package/src/views/udev/dict/childView.vue +184 -184
  168. package/src/views/udev/lov/childView.vue +174 -174
  169. package/src/views/uhome/components/common-menu.vue +118 -0
  170. package/src/views/uhome/components/dynamic-component.vue +66 -0
  171. package/src/views/uhome/components/home-todo.vue +170 -0
  172. package/src/views/uhome/components/menu-favorite.vue +315 -315
  173. package/src/views/uhome/dynamic-card.vue +18 -19
  174. package/src/views/uhome/su-welcome.vue +46 -116
  175. package/src/views/ulogin/login.vue +336 -336
  176. package/src/views/upms/interface/log-in.vue +100 -100
  177. package/src/views/upms/interface/log-out.vue +104 -104
  178. package/src/views/upms/user/login-log.vue +54 -54
  179. package/types/global.d.ts +232 -232
  180. package/dist/await-to-js.es5-BtRbN2QH.js +0 -10
  181. package/dist/layout-home-Cis1KlEr.js +0 -195
  182. package/dist/su-welcome-BXe6Cdp3.js +0 -580
@@ -1,598 +1,598 @@
1
- <script lang="ts">
2
- export default { name: 'LayoutTag' };
3
- </script>
4
- <script setup lang="ts">
5
- import { ref, watch, unref, nextTick, onBeforeUnmount } from 'vue';
6
- import { isEqual, isEmpty } from 'xe-utils';
7
- import { useResizeObserver, useDebounceFn, useFullscreen, onClickOutside } from '@vueuse/core';
8
- import { delay } from '@utogether/utils';
9
- import { emitter } from '../../../utils/mitt';
10
- import { routerArrays } from '../../types';
11
- import { useSettingStoreHook } from '../../../store/modules/settings';
12
- import { handleAliveRoute, getTopMenu } from '../../../router/utils';
13
- import { useMultiTagsStoreHook } from '../../../store/modules/multiTags';
14
- import { usePermissionStoreHook } from '../../../store/modules/permission';
15
- import { useTags } from '../../hooks/useTag';
16
- import { RouteConfigs } from '../../types';
17
- import TagChrome from '../lay-chrome/index.vue';
18
-
19
- const {
20
- route,
21
- router,
22
- visible,
23
- showTags,
24
- instance,
25
- multiTags,
26
- tagsViews,
27
- buttonTop,
28
- buttonLeft,
29
- showModel,
30
- translateX,
31
- isFixedTag,
32
- SuSetting,
33
- activeIndex,
34
- getTabStyle,
35
- iconIsActive,
36
- linkIsActive,
37
- currentSelect,
38
- scheduleIsActive,
39
- getContextMenuStyle,
40
- closeMenu,
41
- onMounted,
42
- onMouseenter,
43
- onMouseleave,
44
- transformI18n,
45
- onContentFullScreen
46
- } = useTags();
47
-
48
- const suSetting = useSettingStoreHook();
49
- const tabDom = ref();
50
- const containerDom = ref();
51
- const scrollbarDom = ref();
52
- const contextmenuRef = ref();
53
- const isShowArrow = ref(false);
54
- const topPath = getTopMenu()?.path;
55
- const { isFullscreen, toggle } = useFullscreen();
56
-
57
- const fixedTags = [...routerArrays, ...usePermissionStoreHook().flatteningRoutes.filter(v => v?.meta?.fixedTag)];
58
- const dynamicTagView = async () => {
59
- await nextTick();
60
- const index = multiTags.value.findIndex(item => {
61
- if (!isEmpty(route.query)) {
62
- return isEqual(route.query, item.query);
63
- } else if (!isEmpty(route.params)) {
64
- return isEqual(route.params, item.params);
65
- } else {
66
- return route.path === item.path;
67
- }
68
- });
69
- moveToView(index);
70
- };
71
-
72
- const moveToView = async (index: number): Promise<void> => {
73
- await nextTick();
74
- const tabNavPadding = 10;
75
- if (!instance.refs['dynamic' + index]) return;
76
- const tabItemEl = instance.refs['dynamic' + index][0];
77
- const tabItemElOffsetLeft = (tabItemEl as HTMLElement)?.offsetLeft;
78
- const tabItemOffsetWidth = (tabItemEl as HTMLElement)?.offsetWidth;
79
- // 标签页导航栏可视长度(不包含溢出部分)
80
- const scrollbarDomWidth = scrollbarDom.value ? scrollbarDom.value.offsetWidth : 0;
81
- // 已有标签页总长度(包含溢出部分)
82
- const tabDomWidth = tabDom.value ? tabDom.value?.offsetWidth : 0;
83
- scrollbarDomWidth <= tabDomWidth ? (isShowArrow.value = true) : (isShowArrow.value = false);
84
- if (tabDomWidth < scrollbarDomWidth || tabItemElOffsetLeft === 0) {
85
- translateX.value = 0;
86
- } else if (tabItemElOffsetLeft < -translateX.value) {
87
- // 标签在可视区域左侧
88
- translateX.value = -tabItemElOffsetLeft + tabNavPadding;
89
- } else if (
90
- tabItemElOffsetLeft > -translateX.value &&
91
- tabItemElOffsetLeft + tabItemOffsetWidth < -translateX.value + scrollbarDomWidth
92
- ) {
93
- // 标签在可视区域
94
- translateX.value = Math.min(0, scrollbarDomWidth - tabItemOffsetWidth - tabItemElOffsetLeft - tabNavPadding);
95
- } else {
96
- // 标签在可视区域右侧
97
- translateX.value = -(tabItemElOffsetLeft - (scrollbarDomWidth - tabNavPadding - tabItemOffsetWidth));
98
- }
99
- };
100
-
101
- const handleScroll = (offset: number): void => {
102
- const scrollbarDomWidth = scrollbarDom.value ? scrollbarDom.value?.offsetWidth : 0;
103
- const tabDomWidth = tabDom.value ? tabDom.value.offsetWidth : 0;
104
- if (offset > 0) {
105
- translateX.value = Math.min(0, translateX.value + offset);
106
- } else {
107
- if (scrollbarDomWidth < tabDomWidth) {
108
- if (translateX.value >= -(tabDomWidth - scrollbarDomWidth)) {
109
- translateX.value = Math.max(translateX.value + offset, scrollbarDomWidth - tabDomWidth);
110
- }
111
- } else {
112
- translateX.value = 0;
113
- }
114
- }
115
- };
116
-
117
- function dynamicRouteTag(value: string): void {
118
- const hasValue = multiTags.value.some(item => {
119
- return item.path === value;
120
- });
121
-
122
- function concatPath(arr: object[], value: string) {
123
- if (!hasValue) {
124
- arr.forEach((arrItem: any) => {
125
- if (arrItem.path === value) {
126
- useMultiTagsStoreHook().handleTags('push', {
127
- path: value,
128
- meta: arrItem.meta,
129
- name: arrItem.name
130
- });
131
- } else {
132
- if (arrItem.children && arrItem.children.length > 0) {
133
- concatPath(arrItem.children, value);
134
- }
135
- }
136
- });
137
- }
138
- }
139
- concatPath(router.options.routes as any, value);
140
- }
141
-
142
- /** 刷新路由 */
143
- function onFresh() {
144
- const { fullPath, query } = unref(route);
145
- router.replace({
146
- path: '/redirect' + fullPath,
147
- query: query
148
- });
149
- handleAliveRoute(route as ToRouteType, 'refresh');
150
- }
151
-
152
- function deleteDynamicTag(obj: any, current: any, tag?: string) {
153
- const valueIndex: number = multiTags.value.findIndex((item: any) => {
154
- if (item.query) {
155
- if (item.path === obj.path) {
156
- return item.query === obj.query;
157
- }
158
- } else if (item.params) {
159
- if (item.path === obj.path) {
160
- return item.params === obj.params;
161
- }
162
- } else {
163
- return item.path === obj.path;
164
- }
165
- });
166
-
167
- const spliceRoute = (startIndex?: number, length?: number, other?: boolean): void => {
168
- if (other) {
169
- useMultiTagsStoreHook().handleTags('equal', [fixedTags, obj].flat());
170
- } else {
171
- useMultiTagsStoreHook().handleTags('splice', '', {
172
- startIndex,
173
- length
174
- }) as any;
175
- }
176
- dynamicTagView();
177
- };
178
-
179
- if (tag === 'other') {
180
- spliceRoute(1, 1, true);
181
- } else if (tag === 'left') {
182
- spliceRoute(fixedTags.length, valueIndex - fixedTags.length);
183
- } else if (tag === 'right') {
184
- spliceRoute(valueIndex + 1, multiTags.value.length);
185
- } else {
186
- // 从当前匹配到的路径中删除
187
- spliceRoute(valueIndex, 1);
188
- }
189
- const newRoute = useMultiTagsStoreHook().handleTags('slice');
190
- if (current === route.path) {
191
- // 如果删除当前激活tag就自动切换到最后一个tag
192
- if (tag === 'left') return;
193
- if (newRoute[0]?.query) {
194
- router.push({ name: newRoute[0].name, query: newRoute[0].query });
195
- } else if (newRoute[0]?.params) {
196
- router.push({ name: newRoute[0].name, params: newRoute[0].params });
197
- } else {
198
- router.push({ path: newRoute[0].path });
199
- }
200
- } else {
201
- if (!multiTags.value.length) return;
202
- if (multiTags.value.some(item => item.path === route.path)) return;
203
- if (newRoute[0]?.query) {
204
- router.push({ name: newRoute[0].name, query: newRoute[0].query });
205
- } else if (newRoute[0]?.params) {
206
- router.push({ name: newRoute[0].name, params: newRoute[0].params });
207
- } else {
208
- router.push({ path: newRoute[0].path });
209
- }
210
- }
211
- }
212
-
213
- function deleteMenu(item, tag?: string) {
214
- deleteDynamicTag(item, item.path, tag);
215
- handleAliveRoute(route as ToRouteType);
216
- }
217
-
218
- function onClickDrop(key, item, selectRoute?: RouteConfigs) {
219
- if (item && item.disabled) return;
220
- let selectTagRoute;
221
- if (selectRoute) {
222
- selectTagRoute = {
223
- path: selectRoute.path,
224
- meta: selectRoute.meta,
225
- name: selectRoute.name,
226
- query: selectRoute?.query,
227
- params: selectRoute?.params
228
- };
229
- } else {
230
- selectTagRoute = { path: route.path, meta: route.meta };
231
- }
232
-
233
- // 当前路由信息
234
- switch (key) {
235
- case 0:
236
- // 刷新路由
237
- onFresh();
238
- break;
239
- case 1:
240
- // 关闭当前标签页
241
- deleteMenu(selectTagRoute);
242
- break;
243
- case 2:
244
- // 关闭左侧标签页
245
- deleteMenu(selectTagRoute, 'left');
246
- break;
247
- case 3:
248
- // 关闭右侧标签页
249
- deleteMenu(selectTagRoute, 'right');
250
- break;
251
- case 4:
252
- // 关闭其他标签页
253
- deleteMenu(selectTagRoute, 'other');
254
- break;
255
- case 5:
256
- // 关闭全部标签页
257
- useMultiTagsStoreHook().handleTags('splice', '', {
258
- startIndex: fixedTags.length,
259
- length: multiTags.value.length
260
- });
261
- router.push(topPath);
262
-
263
- handleAliveRoute(route as ToRouteType);
264
- break;
265
- case 6:
266
- // 整体页面全屏
267
- toggle();
268
- setTimeout(() => {
269
- if (isFullscreen.value) {
270
- tagsViews[6].icon = 'ri:fullscreen-exit-fill';
271
- tagsViews[6].text = 'message.btn.wholeExitFullScreen';
272
- } else {
273
- tagsViews[6].icon = 'ri:fullscreen-fill';
274
- tagsViews[6].text = 'message.btn.wholeFullScreen';
275
- }
276
- }, 100);
277
- break;
278
- case 7:
279
- // 内容区全屏
280
- onContentFullScreen();
281
- setTimeout(() => {
282
- if (SuSetting.hiddenSideBar) {
283
- tagsViews[7].icon = 'ri:fullscreen-exit-fill';
284
- tagsViews[7].text = 'message.btn.contentExitFullScreen';
285
- } else {
286
- tagsViews[7].icon = 'ri:fullscreen-fill';
287
- tagsViews[7].text = 'message.btn.contentFullScreen';
288
- }
289
- }, 100);
290
- break;
291
- }
292
- setTimeout(() => {
293
- showMenuModel(route.fullPath, route.query, route.params);
294
- });
295
- }
296
-
297
- function handleCommand(command: any) {
298
- const { key, item } = command;
299
- onClickDrop(key, item);
300
- }
301
-
302
- /** 触发右键中菜单的点击事件 */
303
- function selectTag(key, item) {
304
- closeMenu();
305
- onClickDrop(key, item, currentSelect.value);
306
- }
307
-
308
- function showMenus(value: boolean) {
309
- Array.of(1, 2, 3, 4, 5).forEach(v => {
310
- tagsViews[v].show = value;
311
- });
312
- }
313
-
314
- function disabledMenus(value: boolean, fixedTag = false) {
315
- Array.of(1, 2, 3, 4, 5).forEach(v => {
316
- tagsViews[v].disabled = value;
317
- });
318
- if (fixedTag) {
319
- tagsViews[2].show = false;
320
- tagsViews[2].disabled = true;
321
- }
322
- }
323
-
324
- /** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是顶级菜单,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */
325
- function showMenuModel(currentPath: string, query: object = {}, params: object = {}, refresh = false) {
326
- const allRoute = multiTags.value;
327
- const routeLength = multiTags.value.length;
328
- let currentIndex = -1;
329
- if (!isEmpty(params)) {
330
- currentIndex = allRoute.findIndex(v => isEqual(v.params, params));
331
- } else if (isEmpty(query)) {
332
- currentIndex = allRoute.findIndex(v => v.path === currentPath);
333
- } else {
334
- currentIndex = allRoute.findIndex(v => isEqual(v.query, query));
335
- }
336
-
337
- function fixedTagDisabled() {
338
- if (allRoute[currentIndex]?.meta?.fixedTag) {
339
- Array.of(1, 2, 3, 4, 5).forEach(v => {
340
- tagsViews[v].disabled = true;
341
- });
342
- }
343
- }
344
-
345
- showMenus(true);
346
-
347
- if (refresh) {
348
- tagsViews[0].show = true;
349
- }
350
-
351
- /**
352
- * currentIndex为1时,左侧的菜单顶级菜单,则不显示关闭左侧标签页
353
- * 如果currentIndex等于routeLength-1,右侧没有菜单,则不显示关闭右侧标签页
354
- */
355
- if (currentIndex === 1 && routeLength !== 2) {
356
- // 左侧的菜单是顶级菜单,右侧存在别的菜单
357
- tagsViews[2].show = false;
358
- Array.of(1, 3, 4, 5).forEach(v => {
359
- tagsViews[v].disabled = false;
360
- });
361
- tagsViews[2].disabled = true;
362
- fixedTagDisabled();
363
- } else if (currentIndex === 1 && routeLength === 2) {
364
- disabledMenus(false);
365
- // 左侧的菜单是顶级菜单,右侧不存在别的菜单
366
- Array.of(2, 3, 4).forEach(v => {
367
- tagsViews[v].show = false;
368
- tagsViews[v].disabled = true;
369
- });
370
- fixedTagDisabled();
371
- } else if (routeLength - 1 === currentIndex && currentIndex !== 0) {
372
- // 当前路由是所有路由中的最后一个
373
- tagsViews[3].show = false;
374
- Array.of(1, 2, 4, 5).forEach(v => {
375
- tagsViews[v].disabled = false;
376
- });
377
- tagsViews[3].disabled = true;
378
- if (allRoute[currentIndex - 1]?.meta?.fixedTag) {
379
- tagsViews[2].show = false;
380
- tagsViews[2].disabled = true;
381
- }
382
- fixedTagDisabled();
383
- } else if (currentIndex === 0 || currentPath === `/redirect${topPath}`) {
384
- // 当前路由为首页
385
- disabledMenus(true);
386
- } else {
387
- disabledMenus(false, allRoute[currentIndex - 1]?.meta?.fixedTag);
388
- fixedTagDisabled();
389
- }
390
- }
391
-
392
- function openMenu(tag, e) {
393
- closeMenu();
394
- if (tag.path === '/welcome' || tag?.meta?.fixedTag) {
395
- // 右键菜单为首页或拥有 fixedTag 属性,只显示刷新
396
- showMenus(false);
397
- tagsViews[0].show = true;
398
- } else if (route.path !== tag.path && route.name !== tag.name) {
399
- // 右键菜单不匹配当前路由,隐藏刷新
400
- tagsViews[0].show = false;
401
- showMenuModel(tag.path, tag.query, tag.params);
402
- } else if (multiTags.value.length === 2 && route.path !== tag.path) {
403
- showMenus(true);
404
- // 只有两个标签时不显示关闭其他标签页
405
- tagsViews[4].show = false;
406
- showMenuModel(tag.path, tag.query, tag.params);
407
- } else if (route.path === tag.path) {
408
- // 右键当前激活的菜单
409
- showMenuModel(tag.path, tag.query, tag.params, true);
410
- }
411
-
412
- currentSelect.value = tag;
413
- const menuMinWidth = 140;
414
- const offsetLeft = unref(containerDom).getBoundingClientRect().left;
415
- const offsetWidth = unref(containerDom).offsetWidth;
416
- const maxLeft = offsetWidth - menuMinWidth;
417
- const left = e.clientX - offsetLeft + 5;
418
- if (left > maxLeft) {
419
- buttonLeft.value = maxLeft;
420
- } else {
421
- buttonLeft.value = left;
422
- }
423
- suSetting.hiddenSideBar ? (buttonTop.value = e.clientY) : (buttonTop.value = e.clientY - 40);
424
- nextTick(() => {
425
- visible.value = true;
426
- });
427
- }
428
-
429
- /** 触发tags标签切换 */
430
- function tagOnClick(item) {
431
- const { name, path } = item;
432
- if (name) {
433
- if (item.query) {
434
- router.push({
435
- name,
436
- query: item.query
437
- });
438
- } else if (item.params) {
439
- router.push({
440
- name,
441
- params: item.params
442
- });
443
- } else {
444
- router.push({ name });
445
- }
446
- } else {
447
- router.push({ path });
448
- }
449
- // showMenuModel(item?.path, item?.query);
450
- emitter.emit('tagOnClick', item);
451
- }
452
-
453
- onClickOutside(contextmenuRef, closeMenu, {
454
- detectIframe: true
455
- });
456
-
457
- watch(route, () => {
458
- activeIndex.value = -1;
459
- dynamicTagView();
460
- });
461
-
462
- watch(isFullscreen, () => {
463
- tagsViews[6].icon = 'ri:fullscreen-fill';
464
- tagsViews[6].text = 'message.wholeFullScreen';
465
- });
466
-
467
- onMounted(() => {
468
- if (!instance) return;
469
-
470
- // 根据当前路由初始化操作标签页的禁用状态
471
- showMenuModel(route.fullPath);
472
-
473
- // 触发隐藏标签页
474
- emitter.on('tagViewsChange', (key: any) => {
475
- if (unref(showTags as any) === key) return;
476
- (showTags as any).value = key;
477
- });
478
-
479
- // 改变标签风格
480
- emitter.on('tagViewsShowModel', key => {
481
- showModel.value = key;
482
- });
483
-
484
- // 接收侧边栏切换传递过来的参数
485
- emitter.on('changLayoutRoute', indexPath => {
486
- dynamicRouteTag(indexPath);
487
- setTimeout(() => {
488
- showMenuModel(indexPath);
489
- });
490
- });
491
-
492
- useResizeObserver(
493
- scrollbarDom,
494
- useDebounceFn(() => {
495
- dynamicTagView();
496
- }, 200)
497
- );
498
- delay().then(() => dynamicTagView());
499
- });
500
-
501
- onBeforeUnmount(() => {
502
- // 解绑`tagViewsChange`、`tagViewsShowModel`、`changLayoutRoute`公共事件,防止多次触发
503
- emitter.off('tagViewsChange');
504
- emitter.off('tagViewsShowModel');
505
- emitter.off('changLayoutRoute');
506
- });
507
- </script>
508
-
509
- <template>
510
- <div v-if="!showTags" ref="containerDom" class="tags-view">
511
- <span v-show="isShowArrow" class="arrow-left">
512
- <IconifyIconOffline icon="ri:arrow-left-s-line" @click="handleScroll(200)" />
513
- </span>
514
- <div ref="scrollbarDom" class="scroll-container" :class="showModel === 'chrome' && 'chrome-scroll-container'">
515
- <div ref="tabDom" class="select-none tab" :style="getTabStyle">
516
- <div
517
- v-for="(item, index) in multiTags"
518
- :key="index"
519
- :ref="'dynamic' + index"
520
- :class="[
521
- 'scroll-item is-closable',
522
- linkIsActive(item),
523
- showModel === 'chrome' && 'chrome-item',
524
- isFixedTag(item) && 'fixed-tag'
525
- ]"
526
- @contextmenu.prevent="openMenu(item, $event)"
527
- @mouseenter.prevent="onMouseenter(index)"
528
- @mouseleave.prevent="onMouseleave(index)"
529
- @click="tagOnClick(item)"
530
- >
531
- <template v-if="showModel !== 'chrome'">
532
- <span class="tag-title dark:text-text_color_primary! dark:hover:text-primary!">
533
- {{ transformI18n(item?.meta?.title) }}
534
- </span>
535
- <span
536
- v-if="isFixedTag(item) ? false : iconIsActive(item, index) || (index === activeIndex && index !== 0)"
537
- class="el-icon-close"
538
- @click.stop="deleteMenu(item)"
539
- >
540
- <IconifyIconOffline icon="ri:close-fill" />
541
- </span>
542
- <span v-if="showModel !== 'card'" :ref="'schedule' + index" :class="[scheduleIsActive(item)]" />
543
- </template>
544
- <div v-else class="chrome-tab">
545
- <div class="chrome-tab__bg">
546
- <TagChrome />
547
- </div>
548
- <span class="tag-title">
549
- {{ transformI18n(item?.meta?.title) }}
550
- </span>
551
- <span v-if="isFixedTag(item) ? false : index !== 0" class="chrome-close-btn" @click.stop="deleteMenu(item)">
552
- <IconifyIconOffline icon="ri:close-fill" />
553
- </span>
554
- <span class="chrome-tab-divider" />
555
- </div>
556
- </div>
557
- </div>
558
- </div>
559
- <span v-show="isShowArrow" class="arrow-right">
560
- <IconifyIconOffline icon="ri:arrow-right-s-line" @click="handleScroll(-200)" />
561
- </span>
562
- <!-- 右键菜单按钮 -->
563
- <transition name="el-zoom-in-top">
564
- <ul v-show="visible" ref="contextmenuRef" :key="Math.random()" :style="getContextMenuStyle" class="contextmenu">
565
- <div v-for="(item, key) in tagsViews.slice(0, 6)" :key="key" style="display: flex; align-items: center">
566
- <li v-if="item.show" @click="selectTag(key, item)">
567
- <IconifyIconOffline :icon="item.icon" />
568
- {{ $t(item.text) }}
569
- </li>
570
- </div>
571
- </ul>
572
- </transition>
573
- <!-- 右侧功能按钮 -->
574
- <el-dropdown trigger="click" placement="bottom-end" @command="handleCommand">
575
- <span class="arrow-down">
576
- <IconifyIconOffline icon="ri:arrow-down-s-line" class="dark:text-white" />
577
- </span>
578
- <template #dropdown>
579
- <el-dropdown-menu>
580
- <el-dropdown-item
581
- v-for="(item, key) in tagsViews"
582
- :key="key"
583
- :command="{ key, item }"
584
- :divided="item.divided"
585
- :disabled="item.disabled"
586
- >
587
- <IconifyIconOffline :icon="item.icon" />
588
- {{ $t(item.text) }}
589
- </el-dropdown-item>
590
- </el-dropdown-menu>
591
- </template>
592
- </el-dropdown>
593
- </div>
594
- </template>
595
-
596
- <style lang="scss" scoped>
597
- @import url('./index.scss');
598
- </style>
1
+ <script lang="ts">
2
+ export default { name: 'LayoutTag' };
3
+ </script>
4
+ <script setup lang="ts">
5
+ import { ref, watch, unref, nextTick, onBeforeUnmount } from 'vue';
6
+ import { isEqual, isEmpty } from 'xe-utils';
7
+ import { useResizeObserver, useDebounceFn, useFullscreen, onClickOutside } from '@vueuse/core';
8
+ import { delay } from '@utogether/utils';
9
+ import { emitter } from '../../../utils/mitt';
10
+ import { routerArrays } from '../../types';
11
+ import { useSettingStoreHook } from '../../../store/modules/settings';
12
+ import { handleAliveRoute, getTopMenu } from '../../../router/utils';
13
+ import { useMultiTagsStoreHook } from '../../../store/modules/multiTags';
14
+ import { usePermissionStoreHook } from '../../../store/modules/permission';
15
+ import { useTags } from '../../hooks/useTag';
16
+ import { RouteConfigs } from '../../types';
17
+ import TagChrome from '../lay-chrome/index.vue';
18
+
19
+ const {
20
+ route,
21
+ router,
22
+ visible,
23
+ showTags,
24
+ instance,
25
+ multiTags,
26
+ tagsViews,
27
+ buttonTop,
28
+ buttonLeft,
29
+ showModel,
30
+ translateX,
31
+ isFixedTag,
32
+ SuSetting,
33
+ activeIndex,
34
+ getTabStyle,
35
+ iconIsActive,
36
+ linkIsActive,
37
+ currentSelect,
38
+ scheduleIsActive,
39
+ getContextMenuStyle,
40
+ closeMenu,
41
+ onMounted,
42
+ onMouseenter,
43
+ onMouseleave,
44
+ transformI18n,
45
+ onContentFullScreen
46
+ } = useTags();
47
+
48
+ const suSetting = useSettingStoreHook();
49
+ const tabDom = ref();
50
+ const containerDom = ref();
51
+ const scrollbarDom = ref();
52
+ const contextmenuRef = ref();
53
+ const isShowArrow = ref(false);
54
+ const topPath = getTopMenu()?.path;
55
+ const { isFullscreen, toggle } = useFullscreen();
56
+
57
+ const fixedTags = [...routerArrays, ...usePermissionStoreHook().flatteningRoutes.filter(v => v?.meta?.fixedTag)];
58
+ const dynamicTagView = async () => {
59
+ await nextTick();
60
+ const index = multiTags.value.findIndex(item => {
61
+ if (!isEmpty(route.query)) {
62
+ return isEqual(route.query, item.query);
63
+ } else if (!isEmpty(route.params)) {
64
+ return isEqual(route.params, item.params);
65
+ } else {
66
+ return route.path === item.path;
67
+ }
68
+ });
69
+ moveToView(index);
70
+ };
71
+
72
+ const moveToView = async (index: number): Promise<void> => {
73
+ await nextTick();
74
+ const tabNavPadding = 10;
75
+ if (!instance.refs['dynamic' + index]) return;
76
+ const tabItemEl = instance.refs['dynamic' + index][0];
77
+ const tabItemElOffsetLeft = (tabItemEl as HTMLElement)?.offsetLeft;
78
+ const tabItemOffsetWidth = (tabItemEl as HTMLElement)?.offsetWidth;
79
+ // 标签页导航栏可视长度(不包含溢出部分)
80
+ const scrollbarDomWidth = scrollbarDom.value ? scrollbarDom.value.offsetWidth : 0;
81
+ // 已有标签页总长度(包含溢出部分)
82
+ const tabDomWidth = tabDom.value ? tabDom.value?.offsetWidth : 0;
83
+ scrollbarDomWidth <= tabDomWidth ? (isShowArrow.value = true) : (isShowArrow.value = false);
84
+ if (tabDomWidth < scrollbarDomWidth || tabItemElOffsetLeft === 0) {
85
+ translateX.value = 0;
86
+ } else if (tabItemElOffsetLeft < -translateX.value) {
87
+ // 标签在可视区域左侧
88
+ translateX.value = -tabItemElOffsetLeft + tabNavPadding;
89
+ } else if (
90
+ tabItemElOffsetLeft > -translateX.value &&
91
+ tabItemElOffsetLeft + tabItemOffsetWidth < -translateX.value + scrollbarDomWidth
92
+ ) {
93
+ // 标签在可视区域
94
+ translateX.value = Math.min(0, scrollbarDomWidth - tabItemOffsetWidth - tabItemElOffsetLeft - tabNavPadding);
95
+ } else {
96
+ // 标签在可视区域右侧
97
+ translateX.value = -(tabItemElOffsetLeft - (scrollbarDomWidth - tabNavPadding - tabItemOffsetWidth));
98
+ }
99
+ };
100
+
101
+ const handleScroll = (offset: number): void => {
102
+ const scrollbarDomWidth = scrollbarDom.value ? scrollbarDom.value?.offsetWidth : 0;
103
+ const tabDomWidth = tabDom.value ? tabDom.value.offsetWidth : 0;
104
+ if (offset > 0) {
105
+ translateX.value = Math.min(0, translateX.value + offset);
106
+ } else {
107
+ if (scrollbarDomWidth < tabDomWidth) {
108
+ if (translateX.value >= -(tabDomWidth - scrollbarDomWidth)) {
109
+ translateX.value = Math.max(translateX.value + offset, scrollbarDomWidth - tabDomWidth);
110
+ }
111
+ } else {
112
+ translateX.value = 0;
113
+ }
114
+ }
115
+ };
116
+
117
+ function dynamicRouteTag(value: string): void {
118
+ const hasValue = multiTags.value.some(item => {
119
+ return item.path === value;
120
+ });
121
+
122
+ function concatPath(arr: object[], value: string) {
123
+ if (!hasValue) {
124
+ arr.forEach((arrItem: any) => {
125
+ if (arrItem.path === value) {
126
+ useMultiTagsStoreHook().handleTags('push', {
127
+ path: value,
128
+ meta: arrItem.meta,
129
+ name: arrItem.name
130
+ });
131
+ } else {
132
+ if (arrItem.children && arrItem.children.length > 0) {
133
+ concatPath(arrItem.children, value);
134
+ }
135
+ }
136
+ });
137
+ }
138
+ }
139
+ concatPath(router.options.routes as any, value);
140
+ }
141
+
142
+ /** 刷新路由 */
143
+ function onFresh() {
144
+ const { fullPath, query } = unref(route);
145
+ router.replace({
146
+ path: '/redirect' + fullPath,
147
+ query: query
148
+ });
149
+ handleAliveRoute(route as ToRouteType, 'refresh');
150
+ }
151
+
152
+ function deleteDynamicTag(obj: any, current: any, tag?: string) {
153
+ const valueIndex: number = multiTags.value.findIndex((item: any) => {
154
+ if (item.query) {
155
+ if (item.path === obj.path) {
156
+ return item.query === obj.query;
157
+ }
158
+ } else if (item.params) {
159
+ if (item.path === obj.path) {
160
+ return item.params === obj.params;
161
+ }
162
+ } else {
163
+ return item.path === obj.path;
164
+ }
165
+ });
166
+
167
+ const spliceRoute = (startIndex?: number, length?: number, other?: boolean): void => {
168
+ if (other) {
169
+ useMultiTagsStoreHook().handleTags('equal', [fixedTags, obj].flat());
170
+ } else {
171
+ useMultiTagsStoreHook().handleTags('splice', '', {
172
+ startIndex,
173
+ length
174
+ }) as any;
175
+ }
176
+ dynamicTagView();
177
+ };
178
+
179
+ if (tag === 'other') {
180
+ spliceRoute(1, 1, true);
181
+ } else if (tag === 'left') {
182
+ spliceRoute(fixedTags.length, valueIndex - fixedTags.length);
183
+ } else if (tag === 'right') {
184
+ spliceRoute(valueIndex + 1, multiTags.value.length);
185
+ } else {
186
+ // 从当前匹配到的路径中删除
187
+ spliceRoute(valueIndex, 1);
188
+ }
189
+ const newRoute = useMultiTagsStoreHook().handleTags('slice');
190
+ if (current === route.path) {
191
+ // 如果删除当前激活tag就自动切换到最后一个tag
192
+ if (tag === 'left') return;
193
+ if (newRoute[0]?.query) {
194
+ router.push({ name: newRoute[0].name, query: newRoute[0].query });
195
+ } else if (newRoute[0]?.params) {
196
+ router.push({ name: newRoute[0].name, params: newRoute[0].params });
197
+ } else {
198
+ router.push({ path: newRoute[0].path });
199
+ }
200
+ } else {
201
+ if (!multiTags.value.length) return;
202
+ if (multiTags.value.some(item => item.path === route.path)) return;
203
+ if (newRoute[0]?.query) {
204
+ router.push({ name: newRoute[0].name, query: newRoute[0].query });
205
+ } else if (newRoute[0]?.params) {
206
+ router.push({ name: newRoute[0].name, params: newRoute[0].params });
207
+ } else {
208
+ router.push({ path: newRoute[0].path });
209
+ }
210
+ }
211
+ }
212
+
213
+ function deleteMenu(item, tag?: string) {
214
+ deleteDynamicTag(item, item.path, tag);
215
+ handleAliveRoute(route as ToRouteType);
216
+ }
217
+
218
+ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
219
+ if (item && item.disabled) return;
220
+ let selectTagRoute;
221
+ if (selectRoute) {
222
+ selectTagRoute = {
223
+ path: selectRoute.path,
224
+ meta: selectRoute.meta,
225
+ name: selectRoute.name,
226
+ query: selectRoute?.query,
227
+ params: selectRoute?.params
228
+ };
229
+ } else {
230
+ selectTagRoute = { path: route.path, meta: route.meta };
231
+ }
232
+
233
+ // 当前路由信息
234
+ switch (key) {
235
+ case 0:
236
+ // 刷新路由
237
+ onFresh();
238
+ break;
239
+ case 1:
240
+ // 关闭当前标签页
241
+ deleteMenu(selectTagRoute);
242
+ break;
243
+ case 2:
244
+ // 关闭左侧标签页
245
+ deleteMenu(selectTagRoute, 'left');
246
+ break;
247
+ case 3:
248
+ // 关闭右侧标签页
249
+ deleteMenu(selectTagRoute, 'right');
250
+ break;
251
+ case 4:
252
+ // 关闭其他标签页
253
+ deleteMenu(selectTagRoute, 'other');
254
+ break;
255
+ case 5:
256
+ // 关闭全部标签页
257
+ useMultiTagsStoreHook().handleTags('splice', '', {
258
+ startIndex: fixedTags.length,
259
+ length: multiTags.value.length
260
+ });
261
+ router.push(topPath);
262
+
263
+ handleAliveRoute(route as ToRouteType);
264
+ break;
265
+ case 6:
266
+ // 整体页面全屏
267
+ toggle();
268
+ setTimeout(() => {
269
+ if (isFullscreen.value) {
270
+ tagsViews[6].icon = 'ri:fullscreen-exit-fill';
271
+ tagsViews[6].text = 'message.btn.wholeExitFullScreen';
272
+ } else {
273
+ tagsViews[6].icon = 'ri:fullscreen-fill';
274
+ tagsViews[6].text = 'message.btn.wholeFullScreen';
275
+ }
276
+ }, 100);
277
+ break;
278
+ case 7:
279
+ // 内容区全屏
280
+ onContentFullScreen();
281
+ setTimeout(() => {
282
+ if (SuSetting.hiddenSideBar) {
283
+ tagsViews[7].icon = 'ri:fullscreen-exit-fill';
284
+ tagsViews[7].text = 'message.btn.contentExitFullScreen';
285
+ } else {
286
+ tagsViews[7].icon = 'ri:fullscreen-fill';
287
+ tagsViews[7].text = 'message.btn.contentFullScreen';
288
+ }
289
+ }, 100);
290
+ break;
291
+ }
292
+ setTimeout(() => {
293
+ showMenuModel(route.fullPath, route.query, route.params);
294
+ });
295
+ }
296
+
297
+ function handleCommand(command: any) {
298
+ const { key, item } = command;
299
+ onClickDrop(key, item);
300
+ }
301
+
302
+ /** 触发右键中菜单的点击事件 */
303
+ function selectTag(key, item) {
304
+ closeMenu();
305
+ onClickDrop(key, item, currentSelect.value);
306
+ }
307
+
308
+ function showMenus(value: boolean) {
309
+ Array.of(1, 2, 3, 4, 5).forEach(v => {
310
+ tagsViews[v].show = value;
311
+ });
312
+ }
313
+
314
+ function disabledMenus(value: boolean, fixedTag = false) {
315
+ Array.of(1, 2, 3, 4, 5).forEach(v => {
316
+ tagsViews[v].disabled = value;
317
+ });
318
+ if (fixedTag) {
319
+ tagsViews[2].show = false;
320
+ tagsViews[2].disabled = true;
321
+ }
322
+ }
323
+
324
+ /** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是顶级菜单,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */
325
+ function showMenuModel(currentPath: string, query: object = {}, params: object = {}, refresh = false) {
326
+ const allRoute = multiTags.value;
327
+ const routeLength = multiTags.value.length;
328
+ let currentIndex = -1;
329
+ if (!isEmpty(params)) {
330
+ currentIndex = allRoute.findIndex(v => isEqual(v.params, params));
331
+ } else if (isEmpty(query)) {
332
+ currentIndex = allRoute.findIndex(v => v.path === currentPath);
333
+ } else {
334
+ currentIndex = allRoute.findIndex(v => isEqual(v.query, query));
335
+ }
336
+
337
+ function fixedTagDisabled() {
338
+ if (allRoute[currentIndex]?.meta?.fixedTag) {
339
+ Array.of(1, 2, 3, 4, 5).forEach(v => {
340
+ tagsViews[v].disabled = true;
341
+ });
342
+ }
343
+ }
344
+
345
+ showMenus(true);
346
+
347
+ if (refresh) {
348
+ tagsViews[0].show = true;
349
+ }
350
+
351
+ /**
352
+ * currentIndex为1时,左侧的菜单顶级菜单,则不显示关闭左侧标签页
353
+ * 如果currentIndex等于routeLength-1,右侧没有菜单,则不显示关闭右侧标签页
354
+ */
355
+ if (currentIndex === 1 && routeLength !== 2) {
356
+ // 左侧的菜单是顶级菜单,右侧存在别的菜单
357
+ tagsViews[2].show = false;
358
+ Array.of(1, 3, 4, 5).forEach(v => {
359
+ tagsViews[v].disabled = false;
360
+ });
361
+ tagsViews[2].disabled = true;
362
+ fixedTagDisabled();
363
+ } else if (currentIndex === 1 && routeLength === 2) {
364
+ disabledMenus(false);
365
+ // 左侧的菜单是顶级菜单,右侧不存在别的菜单
366
+ Array.of(2, 3, 4).forEach(v => {
367
+ tagsViews[v].show = false;
368
+ tagsViews[v].disabled = true;
369
+ });
370
+ fixedTagDisabled();
371
+ } else if (routeLength - 1 === currentIndex && currentIndex !== 0) {
372
+ // 当前路由是所有路由中的最后一个
373
+ tagsViews[3].show = false;
374
+ Array.of(1, 2, 4, 5).forEach(v => {
375
+ tagsViews[v].disabled = false;
376
+ });
377
+ tagsViews[3].disabled = true;
378
+ if (allRoute[currentIndex - 1]?.meta?.fixedTag) {
379
+ tagsViews[2].show = false;
380
+ tagsViews[2].disabled = true;
381
+ }
382
+ fixedTagDisabled();
383
+ } else if (currentIndex === 0 || currentPath === `/redirect${topPath}`) {
384
+ // 当前路由为首页
385
+ disabledMenus(true);
386
+ } else {
387
+ disabledMenus(false, allRoute[currentIndex - 1]?.meta?.fixedTag);
388
+ fixedTagDisabled();
389
+ }
390
+ }
391
+
392
+ function openMenu(tag, e) {
393
+ closeMenu();
394
+ if (tag.path === '/welcome' || tag?.meta?.fixedTag) {
395
+ // 右键菜单为首页或拥有 fixedTag 属性,只显示刷新
396
+ showMenus(false);
397
+ tagsViews[0].show = true;
398
+ } else if (route.path !== tag.path && route.name !== tag.name) {
399
+ // 右键菜单不匹配当前路由,隐藏刷新
400
+ tagsViews[0].show = false;
401
+ showMenuModel(tag.path, tag.query, tag.params);
402
+ } else if (multiTags.value.length === 2 && route.path !== tag.path) {
403
+ showMenus(true);
404
+ // 只有两个标签时不显示关闭其他标签页
405
+ tagsViews[4].show = false;
406
+ showMenuModel(tag.path, tag.query, tag.params);
407
+ } else if (route.path === tag.path) {
408
+ // 右键当前激活的菜单
409
+ showMenuModel(tag.path, tag.query, tag.params, true);
410
+ }
411
+
412
+ currentSelect.value = tag;
413
+ const menuMinWidth = 140;
414
+ const offsetLeft = unref(containerDom).getBoundingClientRect().left;
415
+ const offsetWidth = unref(containerDom).offsetWidth;
416
+ const maxLeft = offsetWidth - menuMinWidth;
417
+ const left = e.clientX - offsetLeft + 5;
418
+ if (left > maxLeft) {
419
+ buttonLeft.value = maxLeft;
420
+ } else {
421
+ buttonLeft.value = left;
422
+ }
423
+ suSetting.hiddenSideBar ? (buttonTop.value = e.clientY) : (buttonTop.value = e.clientY - 40);
424
+ nextTick(() => {
425
+ visible.value = true;
426
+ });
427
+ }
428
+
429
+ /** 触发tags标签切换 */
430
+ function tagOnClick(item) {
431
+ const { name, path } = item;
432
+ if (name) {
433
+ if (item.query) {
434
+ router.push({
435
+ name,
436
+ query: item.query
437
+ });
438
+ } else if (item.params) {
439
+ router.push({
440
+ name,
441
+ params: item.params
442
+ });
443
+ } else {
444
+ router.push({ name });
445
+ }
446
+ } else {
447
+ router.push({ path });
448
+ }
449
+ // showMenuModel(item?.path, item?.query);
450
+ emitter.emit('tagOnClick', item);
451
+ }
452
+
453
+ onClickOutside(contextmenuRef, closeMenu, {
454
+ detectIframe: true
455
+ });
456
+
457
+ watch(route, () => {
458
+ activeIndex.value = -1;
459
+ dynamicTagView();
460
+ });
461
+
462
+ watch(isFullscreen, () => {
463
+ tagsViews[6].icon = 'ri:fullscreen-fill';
464
+ tagsViews[6].text = 'message.wholeFullScreen';
465
+ });
466
+
467
+ onMounted(() => {
468
+ if (!instance) return;
469
+
470
+ // 根据当前路由初始化操作标签页的禁用状态
471
+ showMenuModel(route.fullPath);
472
+
473
+ // 触发隐藏标签页
474
+ emitter.on('tagViewsChange', (key: any) => {
475
+ if (unref(showTags as any) === key) return;
476
+ (showTags as any).value = key;
477
+ });
478
+
479
+ // 改变标签风格
480
+ emitter.on('tagViewsShowModel', key => {
481
+ showModel.value = key;
482
+ });
483
+
484
+ // 接收侧边栏切换传递过来的参数
485
+ emitter.on('changLayoutRoute', indexPath => {
486
+ dynamicRouteTag(indexPath);
487
+ setTimeout(() => {
488
+ showMenuModel(indexPath);
489
+ });
490
+ });
491
+
492
+ useResizeObserver(
493
+ scrollbarDom,
494
+ useDebounceFn(() => {
495
+ dynamicTagView();
496
+ }, 200)
497
+ );
498
+ delay().then(() => dynamicTagView());
499
+ });
500
+
501
+ onBeforeUnmount(() => {
502
+ // 解绑`tagViewsChange`、`tagViewsShowModel`、`changLayoutRoute`公共事件,防止多次触发
503
+ emitter.off('tagViewsChange');
504
+ emitter.off('tagViewsShowModel');
505
+ emitter.off('changLayoutRoute');
506
+ });
507
+ </script>
508
+
509
+ <template>
510
+ <div v-if="!showTags" ref="containerDom" class="tags-view">
511
+ <span v-show="isShowArrow" class="arrow-left">
512
+ <IconifyIconOffline icon="ri:arrow-left-s-line" @click="handleScroll(200)" />
513
+ </span>
514
+ <div ref="scrollbarDom" class="scroll-container" :class="showModel === 'chrome' && 'chrome-scroll-container'">
515
+ <div ref="tabDom" class="select-none tab" :style="getTabStyle">
516
+ <div
517
+ v-for="(item, index) in multiTags"
518
+ :key="index"
519
+ :ref="'dynamic' + index"
520
+ :class="[
521
+ 'scroll-item is-closable',
522
+ linkIsActive(item),
523
+ showModel === 'chrome' && 'chrome-item',
524
+ isFixedTag(item) && 'fixed-tag'
525
+ ]"
526
+ @contextmenu.prevent="openMenu(item, $event)"
527
+ @mouseenter.prevent="onMouseenter(index)"
528
+ @mouseleave.prevent="onMouseleave(index)"
529
+ @click="tagOnClick(item)"
530
+ >
531
+ <template v-if="showModel !== 'chrome'">
532
+ <span class="tag-title dark:text-text_color_primary! dark:hover:text-primary!">
533
+ {{ transformI18n(item?.meta?.title) }}
534
+ </span>
535
+ <span
536
+ v-if="isFixedTag(item) ? false : iconIsActive(item, index) || (index === activeIndex && index !== 0)"
537
+ class="el-icon-close"
538
+ @click.stop="deleteMenu(item)"
539
+ >
540
+ <IconifyIconOffline icon="ri:close-fill" />
541
+ </span>
542
+ <span v-if="showModel !== 'card'" :ref="'schedule' + index" :class="[scheduleIsActive(item)]" />
543
+ </template>
544
+ <div v-else class="chrome-tab">
545
+ <div class="chrome-tab__bg">
546
+ <TagChrome />
547
+ </div>
548
+ <span class="tag-title">
549
+ {{ transformI18n(item?.meta?.title) }}
550
+ </span>
551
+ <span v-if="isFixedTag(item) ? false : index !== 0" class="chrome-close-btn" @click.stop="deleteMenu(item)">
552
+ <IconifyIconOffline icon="ri:close-fill" />
553
+ </span>
554
+ <span class="chrome-tab-divider" />
555
+ </div>
556
+ </div>
557
+ </div>
558
+ </div>
559
+ <span v-show="isShowArrow" class="arrow-right">
560
+ <IconifyIconOffline icon="ri:arrow-right-s-line" @click="handleScroll(-200)" />
561
+ </span>
562
+ <!-- 右键菜单按钮 -->
563
+ <transition name="el-zoom-in-top">
564
+ <ul v-show="visible" ref="contextmenuRef" :key="Math.random()" :style="getContextMenuStyle" class="contextmenu">
565
+ <div v-for="(item, key) in tagsViews.slice(0, 6)" :key="key" style="display: flex; align-items: center">
566
+ <li v-if="item.show" @click="selectTag(key, item)">
567
+ <IconifyIconOffline :icon="item.icon" />
568
+ {{ $t(item.text) }}
569
+ </li>
570
+ </div>
571
+ </ul>
572
+ </transition>
573
+ <!-- 右侧功能按钮 -->
574
+ <el-dropdown trigger="click" placement="bottom-end" @command="handleCommand">
575
+ <span class="arrow-down">
576
+ <IconifyIconOffline icon="ri:arrow-down-s-line" class="dark:text-white" />
577
+ </span>
578
+ <template #dropdown>
579
+ <el-dropdown-menu>
580
+ <el-dropdown-item
581
+ v-for="(item, key) in tagsViews"
582
+ :key="key"
583
+ :command="{ key, item }"
584
+ :divided="item.divided"
585
+ :disabled="item.disabled"
586
+ >
587
+ <IconifyIconOffline :icon="item.icon" />
588
+ {{ $t(item.text) }}
589
+ </el-dropdown-item>
590
+ </el-dropdown-menu>
591
+ </template>
592
+ </el-dropdown>
593
+ </div>
594
+ </template>
595
+
596
+ <style lang="scss" scoped>
597
+ @import url('./index.scss');
598
+ </style>