vft 0.0.1

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 (321) hide show
  1. package/package.json +54 -0
  2. package/src/app/index.ts +3 -0
  3. package/src/app/page-loading/assets/spin.gif +0 -0
  4. package/src/app/page-loading/index.less +10 -0
  5. package/src/app/page-loading/index.ts +3 -0
  6. package/src/app/page-loading/index.vue +38 -0
  7. package/src/app/table/assets/sort_triangle.png +0 -0
  8. package/src/app/table/demos/basic.vue +117 -0
  9. package/src/app/table/demos/complex.vue +2543 -0
  10. package/src/app/table/demos/index.vue +453 -0
  11. package/src/app/table/demos/rightAngle.png +0 -0
  12. package/src/app/table/header.vue +203 -0
  13. package/src/app/table/index.less +119 -0
  14. package/src/app/table/index.ts +5 -0
  15. package/src/app/table/index.vue +478 -0
  16. package/src/app/table/md/api.md +23 -0
  17. package/src/app/table/md/demo.md +3 -0
  18. package/src/app/table/types.ts +45 -0
  19. package/src/common/badge/index.scss +78 -0
  20. package/src/common/badge/index.ts +4 -0
  21. package/src/common/badge/index.vue +111 -0
  22. package/src/common/badge/types.ts +1 -0
  23. package/src/common/clamp/clamp-toggle.vue +91 -0
  24. package/src/common/clamp/index.ts +4 -0
  25. package/src/common/clamp/index.vue +247 -0
  26. package/src/common/code/index.less +321 -0
  27. package/src/common/code/index.ts +3 -0
  28. package/src/common/code/index.vue +60 -0
  29. package/src/common/config-provider/index.ts +4 -0
  30. package/src/common/config-provider/index.vue +94 -0
  31. package/src/common/config-provider/types.ts +29 -0
  32. package/src/common/icon/iconfont/iconfont.css +334 -0
  33. package/src/common/icon/index.scss +31 -0
  34. package/src/common/icon/index.ts +4 -0
  35. package/src/common/icon/index.vue +74 -0
  36. package/src/common/icon/types.ts +16 -0
  37. package/src/common/index.ts +6 -0
  38. package/src/common/message/index.ts +5 -0
  39. package/src/common/message/instance.ts +29 -0
  40. package/src/common/message/message.scss +108 -0
  41. package/src/common/message/message.ts +80 -0
  42. package/src/common/message/message.vue +162 -0
  43. package/src/common/message/method.ts +172 -0
  44. package/src/common/overlay/index.scss +14 -0
  45. package/src/common/overlay/index.ts +3 -0
  46. package/src/common/overlay/index.vue +117 -0
  47. package/src/constants/comp.ts +1 -0
  48. package/src/constants/index.ts +1 -0
  49. package/src/index.ts +3 -0
  50. package/src/page/index.ts +1 -0
  51. package/src/page/page-wrapper/index.scss +31 -0
  52. package/src/page/page-wrapper/index.ts +5 -0
  53. package/src/page/page-wrapper/index.vue +31 -0
  54. package/src/styles/vars.scss +500 -0
  55. package/src/use/index.ts +14 -0
  56. package/src/use/onPopupReopen.ts +15 -0
  57. package/src/use/use-delayed-toggle/index.ts +30 -0
  58. package/src/use/use-floating/index.ts +121 -0
  59. package/src/use/use-forward-ref.ts +35 -0
  60. package/src/use/use-global-config/index.ts +81 -0
  61. package/src/use/use-id/index.ts +43 -0
  62. package/src/use/use-model-toggle/index.ts +151 -0
  63. package/src/use/use-namespace/index.ts +91 -0
  64. package/src/use/use-ordered-children/index.ts +43 -0
  65. package/src/use/use-popper-container/index.ts +43 -0
  66. package/src/use/use-timeout/index.ts +18 -0
  67. package/src/use/use-z-index/index.ts +20 -0
  68. package/src/use/useGlobalZIndex.ts +24 -0
  69. package/src/use/useLazyRender.ts +17 -0
  70. package/src/use/useLockScroll.ts +66 -0
  71. package/src/utils/aria.ts +126 -0
  72. package/src/utils/arrays.ts +13 -0
  73. package/src/utils/error.ts +23 -0
  74. package/src/utils/event.ts +15 -0
  75. package/src/utils/helper.ts +7 -0
  76. package/src/utils/index.ts +8 -0
  77. package/src/utils/interceptor.ts +39 -0
  78. package/src/utils/mount-component.ts +65 -0
  79. package/src/utils/popper.ts +6 -0
  80. package/src/utils/rand.ts +12 -0
  81. package/src/utils/scroll.ts +101 -0
  82. package/src/utils/vnode.ts +169 -0
  83. package/src/web/avatar/index.scss +50 -0
  84. package/src/web/avatar/index.ts +3 -0
  85. package/src/web/avatar/index.vue +83 -0
  86. package/src/web/back-top/index.scss +35 -0
  87. package/src/web/back-top/index.ts +3 -0
  88. package/src/web/back-top/index.vue +72 -0
  89. package/src/web/back-top/types.ts +14 -0
  90. package/src/web/back-top/use-back-top.ts +65 -0
  91. package/src/web/cascader/index.scss +214 -0
  92. package/src/web/cascader/index.ts +5 -0
  93. package/src/web/cascader/index.vue +767 -0
  94. package/src/web/cascader-panel/config.ts +44 -0
  95. package/src/web/cascader-panel/index.scss +134 -0
  96. package/src/web/cascader-panel/index.ts +5 -0
  97. package/src/web/cascader-panel/index.vue +319 -0
  98. package/src/web/cascader-panel/menu.vue +135 -0
  99. package/src/web/cascader-panel/node-content.ts +23 -0
  100. package/src/web/cascader-panel/node.ts +218 -0
  101. package/src/web/cascader-panel/node.vue +197 -0
  102. package/src/web/cascader-panel/store.ts +83 -0
  103. package/src/web/cascader-panel/types.ts +56 -0
  104. package/src/web/cascader-panel/utils.ts +40 -0
  105. package/src/web/cascader-select/README.md +31 -0
  106. package/src/web/cascader-select/index.scss +54 -0
  107. package/src/web/cascader-select/index.ts +5 -0
  108. package/src/web/cascader-select/index.vue +132 -0
  109. package/src/web/cascader-select/types.ts +9 -0
  110. package/src/web/context-menu/createContextMenu.ts +72 -0
  111. package/src/web/context-menu/index.ts +4 -0
  112. package/src/web/context-menu/index.vue +89 -0
  113. package/src/web/context-menu/types.ts +27 -0
  114. package/src/web/context-menu/useContextMenu.ts +14 -0
  115. package/src/web/descriptions/description-item.vue +34 -0
  116. package/src/web/descriptions/description.vue +124 -0
  117. package/src/web/descriptions/descriptions-cell.ts +95 -0
  118. package/src/web/descriptions/descriptions-item.scss +68 -0
  119. package/src/web/descriptions/descriptions-row.vue +49 -0
  120. package/src/web/descriptions/descriptions.scss +153 -0
  121. package/src/web/descriptions/descriptions.type.ts +19 -0
  122. package/src/web/descriptions/index.ts +4 -0
  123. package/src/web/descriptions/token.ts +4 -0
  124. package/src/web/divider/index.scss +53 -0
  125. package/src/web/divider/index.ts +5 -0
  126. package/src/web/divider/index.vue +60 -0
  127. package/src/web/divider/types.ts +2 -0
  128. package/src/web/empty/assets/no-collect.png +0 -0
  129. package/src/web/empty/assets/no-data.png +0 -0
  130. package/src/web/empty/assets/no-filter.png +0 -0
  131. package/src/web/empty/assets/no-page-data.png +0 -0
  132. package/src/web/empty/assets/no-search.png +0 -0
  133. package/src/web/empty/constants.ts +12 -0
  134. package/src/web/empty/index.scss +57 -0
  135. package/src/web/empty/index.ts +5 -0
  136. package/src/web/empty/index.vue +96 -0
  137. package/src/web/exception/exception.png +0 -0
  138. package/src/web/exception/index.ts +3 -0
  139. package/src/web/exception/index.vue +44 -0
  140. package/src/web/filter/README.md +25 -0
  141. package/src/web/filter/index.scss +14 -0
  142. package/src/web/filter/index.ts +5 -0
  143. package/src/web/filter/index.vue +60 -0
  144. package/src/web/filter/type.ts +13 -0
  145. package/src/web/focus-trap/index.ts +6 -0
  146. package/src/web/focus-trap/index.vue +328 -0
  147. package/src/web/focus-trap/tokens.ts +23 -0
  148. package/src/web/focus-trap/utils.ts +178 -0
  149. package/src/web/full-screen/index.scss +22 -0
  150. package/src/web/full-screen/index.ts +3 -0
  151. package/src/web/full-screen/index.vue +24 -0
  152. package/src/web/icon-text/index.ts +3 -0
  153. package/src/web/icon-text/index.vue +77 -0
  154. package/src/web/image/index.scss +46 -0
  155. package/src/web/image/index.ts +5 -0
  156. package/src/web/image/index.vue +251 -0
  157. package/src/web/image/types.ts +1 -0
  158. package/src/web/index.ts +33 -0
  159. package/src/web/input/index.scss +473 -0
  160. package/src/web/input/index.ts +3 -0
  161. package/src/web/input/index.vue +533 -0
  162. package/src/web/input/utils.ts +102 -0
  163. package/src/web/layouts/blank.vue +4 -0
  164. package/src/web/layouts/footer/index.scss +31 -0
  165. package/src/web/layouts/footer/index.ts +3 -0
  166. package/src/web/layouts/footer/index.vue +38 -0
  167. package/src/web/layouts/header/index.scss +35 -0
  168. package/src/web/layouts/header/index.ts +3 -0
  169. package/src/web/layouts/header/index.vue +47 -0
  170. package/src/web/layouts/iframe/index.scss +18 -0
  171. package/src/web/layouts/iframe/index.vue +36 -0
  172. package/src/web/layouts/iframe/page.vue +30 -0
  173. package/src/web/layouts/index.ts +8 -0
  174. package/src/web/layouts/router-view-content/index.vue +70 -0
  175. package/src/web/link/index.scss +95 -0
  176. package/src/web/link/index.ts +3 -0
  177. package/src/web/link/index.vue +68 -0
  178. package/src/web/loading/directive.ts +104 -0
  179. package/src/web/loading/index.ts +6 -0
  180. package/src/web/loading/loading.scss +108 -0
  181. package/src/web/loading/loading.ts +156 -0
  182. package/src/web/loading/service.ts +145 -0
  183. package/src/web/loading/types.ts +29 -0
  184. package/src/web/logo/index.scss +31 -0
  185. package/src/web/logo/index.ts +5 -0
  186. package/src/web/logo/index.vue +45 -0
  187. package/src/web/logo/types.ts +6 -0
  188. package/src/web/menu/index.scss +336 -0
  189. package/src/web/menu/index.ts +8 -0
  190. package/src/web/menu/menu-collapse-transition.vue +62 -0
  191. package/src/web/menu/menu-item-group.vue +27 -0
  192. package/src/web/menu/menu-item.vue +126 -0
  193. package/src/web/menu/menu.vue +459 -0
  194. package/src/web/menu/sub-menu.vue +440 -0
  195. package/src/web/menu/types.ts +66 -0
  196. package/src/web/menu/use-menu-css-var.ts +11 -0
  197. package/src/web/menu/use-menu.ts +60 -0
  198. package/src/web/menu/utils/menu-bar.ts +19 -0
  199. package/src/web/menu/utils/menu-item.ts +55 -0
  200. package/src/web/menu/utils/submenu.ts +66 -0
  201. package/src/web/multiple-select-flat/index.ts +5 -0
  202. package/src/web/multiple-select-flat/index.vue +53 -0
  203. package/src/web/multiple-select-flat/types.ts +5 -0
  204. package/src/web/multiple-tabs/index.scss +16 -0
  205. package/src/web/multiple-tabs/index.ts +5 -0
  206. package/src/web/multiple-tabs/index.vue +193 -0
  207. package/src/web/multiple-tabs/tab-content.vue +40 -0
  208. package/src/web/multiple-tabs/types.ts +3 -0
  209. package/src/web/multiple-tabs/use/index.ts +2 -0
  210. package/src/web/multiple-tabs/use/use-multiple-tabs.ts +86 -0
  211. package/src/web/multiple-tabs/use/use-tab-dropdown.ts +101 -0
  212. package/src/web/nodata/README.md +42 -0
  213. package/src/web/nodata/fail.vue +13 -0
  214. package/src/web/nodata/img/100.png +0 -0
  215. package/src/web/nodata/img/101.png +0 -0
  216. package/src/web/nodata/img/102.png +0 -0
  217. package/src/web/nodata/img/103.png +0 -0
  218. package/src/web/nodata/img/104.png +0 -0
  219. package/src/web/nodata/img/105.png +0 -0
  220. package/src/web/nodata/img/106.png +0 -0
  221. package/src/web/nodata/img/107.png +0 -0
  222. package/src/web/nodata/img/200.png +0 -0
  223. package/src/web/nodata/img/201.png +0 -0
  224. package/src/web/nodata/img/202.png +0 -0
  225. package/src/web/nodata/img/203.png +0 -0
  226. package/src/web/nodata/index.scss +37 -0
  227. package/src/web/nodata/index.ts +6 -0
  228. package/src/web/nodata/index.vue +46 -0
  229. package/src/web/nodata/types.ts +17 -0
  230. package/src/web/only-child/index.tsx +69 -0
  231. package/src/web/pagination/components/jumper.vue +49 -0
  232. package/src/web/pagination/components/next.vue +40 -0
  233. package/src/web/pagination/components/pager.vue +215 -0
  234. package/src/web/pagination/components/prev.vue +35 -0
  235. package/src/web/pagination/components/sizes.vue +76 -0
  236. package/src/web/pagination/components/total.vue +21 -0
  237. package/src/web/pagination/index.scss +231 -0
  238. package/src/web/pagination/index.ts +5 -0
  239. package/src/web/pagination/pagination.ts +363 -0
  240. package/src/web/pagination/usePagination.ts +13 -0
  241. package/src/web/popover/directive.ts +21 -0
  242. package/src/web/popover/index.scss +58 -0
  243. package/src/web/popover/index.ts +3 -0
  244. package/src/web/popover/index.vue +161 -0
  245. package/src/web/popover/types.ts +26 -0
  246. package/src/web/popper/arrow.vue +45 -0
  247. package/src/web/popper/content.vue +311 -0
  248. package/src/web/popper/index.scss +108 -0
  249. package/src/web/popper/index.ts +11 -0
  250. package/src/web/popper/popper.vue +57 -0
  251. package/src/web/popper/tokens.ts +28 -0
  252. package/src/web/popper/trigger.vue +166 -0
  253. package/src/web/popper/types.ts +49 -0
  254. package/src/web/popper/utils.ts +81 -0
  255. package/src/web/qrcode/drawCanvas.ts +32 -0
  256. package/src/web/qrcode/drawLogo.ts +82 -0
  257. package/src/web/qrcode/index.ts +5 -0
  258. package/src/web/qrcode/index.vue +107 -0
  259. package/src/web/qrcode/qrcodePlus.ts +4 -0
  260. package/src/web/qrcode/toCanvas.ts +11 -0
  261. package/src/web/qrcode/types.ts +38 -0
  262. package/src/web/result/index.scss +69 -0
  263. package/src/web/result/index.ts +3 -0
  264. package/src/web/result/index.vue +63 -0
  265. package/src/web/scrollbar/bar.vue +48 -0
  266. package/src/web/scrollbar/index.scss +91 -0
  267. package/src/web/scrollbar/index.ts +5 -0
  268. package/src/web/scrollbar/index.vue +236 -0
  269. package/src/web/scrollbar/thumb.vue +183 -0
  270. package/src/web/scrollbar/tokens.ts +10 -0
  271. package/src/web/scrollbar/types.ts +7 -0
  272. package/src/web/scrollbar/util.ts +38 -0
  273. package/src/web/select/constants.ts +13 -0
  274. package/src/web/select/index.ts +11 -0
  275. package/src/web/select/index.vue +555 -0
  276. package/src/web/select/option-group.scss +49 -0
  277. package/src/web/select/option-group.vue +97 -0
  278. package/src/web/select/option-item.scss +66 -0
  279. package/src/web/select/option.scss +32 -0
  280. package/src/web/select/option.vue +110 -0
  281. package/src/web/select/select-dropdown.scss +86 -0
  282. package/src/web/select/select-dropdown.vue +51 -0
  283. package/src/web/select/select.scss +213 -0
  284. package/src/web/select/token.ts +56 -0
  285. package/src/web/select/useOption.ts +146 -0
  286. package/src/web/select/useSelect.ts +942 -0
  287. package/src/web/select/utils.ts +5 -0
  288. package/src/web/side-menu/index.scss +66 -0
  289. package/src/web/side-menu/index.ts +4 -0
  290. package/src/web/side-menu/index.vue +228 -0
  291. package/src/web/side-menu/types.ts +20 -0
  292. package/src/web/single-select/index.scss +60 -0
  293. package/src/web/single-select/index.ts +5 -0
  294. package/src/web/single-select/index.vue +70 -0
  295. package/src/web/single-select/select@2x.png +0 -0
  296. package/src/web/single-select/types.ts +5 -0
  297. package/src/web/svg/index.ts +3 -0
  298. package/src/web/svg/index.vue +22 -0
  299. package/src/web/tabs/index.scss +579 -0
  300. package/src/web/tabs/index.ts +6 -0
  301. package/src/web/tabs/index.vue +236 -0
  302. package/src/web/tabs/tab-bar.vue +90 -0
  303. package/src/web/tabs/tab-nav.vue +403 -0
  304. package/src/web/tabs/tab-pane.vue +90 -0
  305. package/src/web/tabs/types.ts +66 -0
  306. package/src/web/tag/index.scss +182 -0
  307. package/src/web/tag/index.ts +5 -0
  308. package/src/web/tag/index.vue +78 -0
  309. package/src/web/tag/types.ts +2 -0
  310. package/src/web/tooltip/content.vue +239 -0
  311. package/src/web/tooltip/index.ts +4 -0
  312. package/src/web/tooltip/tokens.ts +21 -0
  313. package/src/web/tooltip/tooltip.vue +270 -0
  314. package/src/web/tooltip/trigger.vue +119 -0
  315. package/src/web/tooltip/types.ts +56 -0
  316. package/src/web/tooltip/utils.ts +20 -0
  317. package/src/web/transition/collapse-transition.vue +73 -0
  318. package/src/web/transition/index.ts +5 -0
  319. package/tsconfig.json +8 -0
  320. package/types/component.ts +1 -0
  321. package/types/index.d.ts +286 -0
@@ -0,0 +1,459 @@
1
+ <script lang="ts" setup>
2
+ import {
3
+ computed,
4
+ getCurrentInstance,
5
+ h,
6
+ nextTick,
7
+ onMounted,
8
+ provide,
9
+ reactive,
10
+ ref,
11
+ watch,
12
+ watchEffect,
13
+ useSlots
14
+ } from 'vue';
15
+ import { useResizeObserver } from '@vueuse/core';
16
+ import { flattedChildren } from '../../utils';
17
+ import { useNamespace } from '../../use';
18
+ import Menubar from './utils/menu-bar';
19
+ import MenuCollapseTransition from './menu-collapse-transition.vue';
20
+ import VriSubMenu from './sub-menu.vue';
21
+ import { useMenuCssVar } from './use-menu-css-var';
22
+ import type { MenuProvider, SubMenuProvider } from './types';
23
+ import type { Router } from 'vue-router';
24
+ import type { VNode, VNodeArrayChildren } from 'vue';
25
+ import type { UseResizeObserverReturn } from '@vueuse/core';
26
+ import './index.scss';
27
+ import Icon from '../../common/icon/index.vue';
28
+
29
+ interface MenuProps {
30
+ /** 菜单展示模式 */
31
+ mode?: 'horizontal' | 'vertical';
32
+ /** 页面加载时默认激活菜单的 index(menu-item 的index) */
33
+ defaultActive?: string;
34
+ /** 默认打开的 sub-menu 的 index 的数组 */
35
+ defaultOpeneds?: string[];
36
+ /** 是否只保持一个子菜单的展开 */
37
+ uniqueOpened?: boolean;
38
+ /** 是否启用 vue-router 模式。 启用该模式会在激活导航时以 index 作为 path 进行路由跳转 使用 default-active 来设置加载时的激活项 */
39
+ router?: boolean;
40
+ /** 子菜单打开的触发方式,只在 mode 为 horizontal 时有效 */
41
+ menuTrigger?: 'hover' | 'click';
42
+ collapse?: boolean;
43
+ /** 是否水平折叠收起菜单(仅在 mode 为 vertical 时可用) */
44
+ backgroundColor?: string;
45
+ textColor?: string;
46
+ activeTextColor?: string;
47
+ /** 是否开启折叠动画 */
48
+ collapseTransition?: boolean;
49
+ /** 是否省略多余的子项(仅在横向模式生效) */
50
+ ellipsis?: boolean;
51
+ }
52
+
53
+ const {
54
+ mode = 'vertical',
55
+ defaultActive = '',
56
+ defaultOpeneds = [],
57
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
58
+ menuTrigger = 'hover',
59
+ collapseTransition = true,
60
+ ellipsis = true,
61
+ uniqueOpened,
62
+ collapse,
63
+ router
64
+ } = defineProps<MenuProps>();
65
+
66
+ const emit = defineEmits([
67
+ 'close',
68
+ 'open',
69
+ 'select'
70
+ ]);
71
+
72
+ const ns = /* hoist-static*/ useNamespace('menu');
73
+
74
+ defineOptions({
75
+ name: ns.b()
76
+ });
77
+
78
+ const slots = useSlots();
79
+
80
+ const instance = getCurrentInstance()!;
81
+
82
+ // get router instance
83
+ const _router = instance.appContext.config.globalProperties.$router as Router;
84
+
85
+ const menu = ref<HTMLUListElement>();
86
+
87
+ const nsSubMenu = useNamespace('sub-menu');
88
+
89
+ // data
90
+ const sliceIndex = ref(-1);
91
+
92
+ // 展开的菜单项数组 defaultOpeneds 配置的对应 sub-menu 中的数组,并且菜单不能是收缩状态
93
+ const openedMenus = ref<MenuProvider['openedMenus']>(
94
+ defaultOpeneds && !collapse ? defaultOpeneds.slice(0) : []
95
+ );
96
+
97
+ // 页面加载时默认激活菜单的 index,最终提供给 menu-item 去使用
98
+ const activeIndex = ref<MenuProvider['activeIndex']>(defaultActive);
99
+
100
+ /**
101
+ * 主要是存储 menu-item 中的菜单项
102
+ * 格式如下
103
+ * ```
104
+ * {
105
+ * 1-1:{index: '1-1', indexPath: ['mio', '1-1'], active: false},
106
+ * 1-2:{index: '1-2', indexPath: ['mio', '1-2'], active: false}
107
+ * }
108
+ * ```
109
+ */
110
+ const items = ref<MenuProvider['items']>({});
111
+
112
+ /**
113
+ * 主要是存储 sub-menu 中的菜单项
114
+ * 格式如下
115
+ * ```
116
+ * {
117
+ * mio:{index: 'mio', indexPath: ['mio'], active: false},
118
+ * test:{index: 'test', indexPath: ['test'], active: false}
119
+ * }
120
+ * ```
121
+ */
122
+ const subMenus = ref<MenuProvider['subMenus']>({});
123
+
124
+ /**
125
+ * @description 是否需要 tooltip 1.水平模式的菜单需要 2.竖直类型的菜单在收缩时需要
126
+ * @author wfd
127
+ * @date 2022/11/8 13:22
128
+ * @example
129
+ * @type {ComputedRef<MenuProvider["isMenuPopup"]>}
130
+ */
131
+ const isMenuPopup = computed<MenuProvider['isMenuPopup']>(() => {
132
+ return mode === 'horizontal' || (mode === 'vertical' && collapse) as MenuProvider['isMenuPopup'];
133
+ });
134
+
135
+ // 当配置了 defaultActive 时菜单的展开设置
136
+ const initMenu = () => {
137
+ // 如果设置了 activeIndex 且当前的 activeIndex 项在 items 中存在
138
+ // 获取当前激活菜单的 item {index: '1-1', indexPath: ['mio', '1-1'], active: false}
139
+ const activeItem = activeIndex.value && items.value[activeIndex.value];
140
+ // 只在竖直模式,且非折叠模式,且子菜单配置为激活时触发
141
+ if (!activeItem || mode === 'horizontal' || collapse) return;
142
+ // ['mio', '1-1']
143
+ const indexPath = activeItem.indexPath;
144
+
145
+ // 展开该菜单项的路径上所有子菜单
146
+ indexPath.forEach((index) => {
147
+ // {index: 'mio', indexPath: ['mio'], active: false}
148
+ const subMenu = subMenus.value[index];
149
+ subMenu && openMenu(index, subMenu.indexPath);
150
+ });
151
+ };
152
+
153
+ /**
154
+ * @description 展开子菜单
155
+ * @author wfd
156
+ * @date 2022/11/8 16:44
157
+ * @example
158
+ * @param {string} index
159
+ * @param {string[]} indexPath
160
+ */
161
+ const openMenu: MenuProvider['openMenu'] = (index: string, indexPath: string[]) => {
162
+ // 如果当前点击的 index 在 openedMenus 中,则不向下执行操作
163
+ if (openedMenus.value.includes(index)) return;
164
+ // 将不在该菜单路径下的其余菜单收起
165
+ if (uniqueOpened) {
166
+ openedMenus.value = openedMenus.value.filter((index: string) => indexPath.includes(index));
167
+ }
168
+ // 将点击的 index 加入到 openedMenus 中
169
+ openedMenus.value.push(index);
170
+ emit('open', index, indexPath);
171
+ };
172
+
173
+ /**
174
+ * @description 关闭当前点击的 sub-menu 对应的 index,主要是从 openedMenus 移除该项
175
+ * @author wfd
176
+ * @date 2022/11/8 16:43
177
+ * @example
178
+ * @param {string} index
179
+ * @param {string[]} indexPath
180
+ */
181
+ const closeMenu: MenuProvider['closeMenu'] = (index, indexPath) => {
182
+ const i = openedMenus.value.indexOf(index);
183
+ if (i !== -1) {
184
+ openedMenus.value.splice(i, 1);
185
+ }
186
+ emit('close', index, indexPath);
187
+ };
188
+
189
+ // sub-menu 点击事件,主要是展开收缩菜单,通过控制 openedMenus 的值
190
+ const handleSubMenuClick: MenuProvider['handleSubMenuClick'] = ({ index, indexPath }) => {
191
+ // 判断当前点击的 sub-menu 菜单是否已经在 openedMenus 中
192
+ const isOpened = openedMenus.value.includes(index);
193
+
194
+ // 如果已经在 openedMenus 中,代表当前菜单已经展开(展开则调用 close 关闭),否则未打开(则调用 open 给展开)
195
+ if (isOpened) {
196
+ closeMenu(index, indexPath);
197
+ } else {
198
+ openMenu(index, indexPath);
199
+ }
200
+ };
201
+
202
+ /**
203
+ * @description 子项点击跳转事件,如果 menu.vue 配置了 route 为 true,则执行路由跳转,否则触发
204
+ * emit('select') 只返回数据
205
+ * @author wfd
206
+ * @date 2022/11/8 18:30
207
+ * @example
208
+ * @param {MenuItemClicked} menuItem
209
+ */
210
+ const handleMenuItemClick: MenuProvider['handleMenuItemClick'] = (menuItem) => {
211
+
212
+ if (mode === 'horizontal' || collapse) {
213
+ openedMenus.value = [];
214
+ }
215
+ const { index, indexPath } = menuItem;
216
+ if (index === undefined || indexPath === undefined) return;
217
+
218
+ if (router && _router) {
219
+ // 路由跳转的路径如果 menu-item 配置了则,采用 menu-item 的,否则采用 index 为 router 路径
220
+ const route = menuItem.route || index;
221
+ const routerResult = _router.push(route).then((res) => {
222
+ if (!res) activeIndex.value = index;
223
+ return res;
224
+ });
225
+ emit('select', menuItem, routerResult);
226
+ } else {
227
+ activeIndex.value = index;
228
+
229
+ emit('select', menuItem);
230
+ }
231
+ };
232
+
233
+ /**
234
+ * 更新 activeIndex
235
+ * @param {string} val
236
+ */
237
+ const updateActiveIndex = (val: string) => {
238
+ const itemsInData = items.value;
239
+ // 首先从最新的 val 中去 items 取数据,如果没有取到再去 items 对应的 activeIndex 去取
240
+ const item = itemsInData[val] || (activeIndex.value && itemsInData[activeIndex.value]) || itemsInData[defaultActive];
241
+
242
+ // 更新 activeIndex
243
+ if (item) {
244
+ activeIndex.value = item.index;
245
+ } else {
246
+ activeIndex.value = val;
247
+ }
248
+ };
249
+
250
+ const calcSliceIndex = () => {
251
+ if (!menu.value) return -1;
252
+ const items = Array.from(menu.value?.childNodes ?? []).filter(
253
+ (item) => item.nodeName !== '#text' || item.nodeValue) as HTMLElement[];
254
+ const moreItemWidth = 64;
255
+ const paddingLeft = Number.parseInt(getComputedStyle(menu.value!).paddingLeft, 10);
256
+ const paddingRight = Number.parseInt(getComputedStyle(menu.value!).paddingRight, 10);
257
+ const menuWidth = menu.value!.clientWidth - paddingLeft - paddingRight;
258
+ let calcWidth = 0;
259
+ let sliceIndex = 0;
260
+ items.forEach((item, index) => {
261
+ calcWidth += item.offsetWidth || 0;
262
+ if (calcWidth <= menuWidth - moreItemWidth) {
263
+ sliceIndex = index + 1;
264
+ }
265
+ });
266
+ return sliceIndex === items.length ? -1 : sliceIndex;
267
+ };
268
+
269
+ // 常见的计算机监视器FPS为60Hz,这意味着每秒重画60次。计算公式:1000ms60≈ 16.67ms,为了避免“调整大小”时重复触发的特定机会,将等待设置为16.67 2=33.34
270
+ const debounce = (fn: () => void, wait = 33.34) => {
271
+ let timmer: ReturnType<typeof setTimeout> | null;
272
+ return () => {
273
+ timmer && clearTimeout(timmer);
274
+ timmer = setTimeout(() => {
275
+ fn();
276
+ }, wait);
277
+ };
278
+ };
279
+
280
+ let isFirstTimeRender = true;
281
+ const handleResize = () => {
282
+ const callback = () => {
283
+ sliceIndex.value = -1;
284
+ nextTick(() => {
285
+ sliceIndex.value = calcSliceIndex();
286
+ });
287
+ };
288
+ // 第一次调整大小时直接执行回调以避免抖动
289
+ isFirstTimeRender ? callback() : debounce(callback)();
290
+ isFirstTimeRender = false;
291
+ };
292
+
293
+ // defaultActive 变化时触发时更新 activeIndex
294
+ watch(
295
+ () => defaultActive,
296
+ (currentActive) => {
297
+ // 如果最新的 defaultActive 在 items 中不存在则 activeIndex 更新为空
298
+ if (!items.value[currentActive]) {
299
+ activeIndex.value = '';
300
+ }
301
+ updateActiveIndex(currentActive);
302
+ }
303
+ );
304
+
305
+ watch(
306
+ () => collapse,
307
+ (value) => {
308
+ if (value) openedMenus.value = [];
309
+ }
310
+ );
311
+
312
+ // 监控 items 的变化(在 menu-item.vue 中的 onMounted 中执行产生变化),然后执行 initMenu
313
+ watch(items.value, initMenu);
314
+
315
+ let resizeStopper: UseResizeObserverReturn['stop'];
316
+ watchEffect(() => {
317
+ if (mode === 'horizontal' && ellipsis) resizeStopper = useResizeObserver(menu, handleResize).stop;
318
+ else resizeStopper?.();
319
+ });
320
+
321
+ // 提供给 sub-menu.vue 使用,将每一个 sub-menu 的数据存储在 subMenus 中
322
+ const addSubMenu: MenuProvider['addSubMenu'] = (item) => {
323
+ subMenus.value[item.index] = item;
324
+ };
325
+
326
+ const removeSubMenu: MenuProvider['removeSubMenu'] = (item) => {
327
+ delete subMenus.value[item.index];
328
+ };
329
+
330
+ /**
331
+ * 此函数在 menu-item 中触发,items.value 默认为空对象
332
+ * 最后添加后的 items 格式如下
333
+ * ```
334
+ * {
335
+ * 1-1:{index: '1-1', indexPath: ['mio', '1-1'], active: false},
336
+ * 1-2:{index: '1-2', indexPath: ['mio', '1-2'], active: false}
337
+ * }
338
+ * ```
339
+ * @param {MenuItemRegistered} item
340
+ */
341
+ const addMenuItem: MenuProvider['addMenuItem'] = (item) => {
342
+ items.value[item.index] = item;
343
+ };
344
+
345
+ const removeMenuItem: MenuProvider['removeMenuItem'] = (item) => {
346
+ delete items.value[item.index];
347
+ };
348
+
349
+ /**
350
+ * 提供以下方法与属性给 sub-menu.vue 使用
351
+ */
352
+ provide<MenuProvider>(
353
+ 'rootMenu',
354
+ reactive({
355
+ props: instance.props,
356
+ openedMenus,
357
+ items,
358
+ subMenus,
359
+ activeIndex,
360
+ isMenuPopup,
361
+
362
+ addMenuItem,
363
+ removeMenuItem,
364
+ addSubMenu,
365
+ removeSubMenu,
366
+ openMenu,
367
+ closeMenu,
368
+ handleMenuItemClick,
369
+ handleSubMenuClick
370
+ })
371
+ );
372
+
373
+ provide<SubMenuProvider>(`subMenu:${instance.uid}`, {
374
+ addSubMenu,
375
+ removeSubMenu,
376
+ mouseInChild: ref(false),
377
+ // 表示当前是第一级的 sub-menu
378
+ level: 0
379
+ });
380
+
381
+ onMounted(() => {
382
+ if (mode === 'horizontal') {
383
+ new Menubar(instance.vnode.el!, ns.namespace.value);
384
+ }
385
+ });
386
+
387
+ // 触发展开菜单事件
388
+ const open = (index: string) => {
389
+ const { indexPath } = subMenus.value[index];
390
+ indexPath.forEach((i) => openMenu(i, indexPath));
391
+ };
392
+
393
+ defineExpose({
394
+ open,
395
+ close: closeMenu,
396
+ handleResize
397
+ });
398
+
399
+ defineRender(() => {
400
+ let slot: VNodeArrayChildren = slots.default?.() ?? [];
401
+ const vShowMore: VNode[] = [];
402
+
403
+ // 水平默认下执行
404
+ if (mode === 'horizontal' && menu.value) {
405
+ const originalSlot = flattedChildren(slot) as VNodeArrayChildren;
406
+ const slotDefault = sliceIndex.value === -1 ? originalSlot : originalSlot.slice(0,
407
+ sliceIndex.value);
408
+
409
+ const slotMore = sliceIndex.value === -1 ? [] : originalSlot.slice(sliceIndex.value);
410
+
411
+ if (slotMore?.length && ellipsis) {
412
+ slot = slotDefault;
413
+ vShowMore.push(
414
+ h(
415
+ VriSubMenu,
416
+ {
417
+ index: 'sub-menu-more',
418
+ class: nsSubMenu.e('hide-arrow')
419
+ },
420
+ {
421
+ title: () => h(
422
+ Icon,
423
+ {
424
+ icon: 'vi-item',
425
+ class: nsSubMenu.e('icon-more')
426
+ }
427
+ ),
428
+ default: () => slotMore
429
+ }
430
+ )
431
+ );
432
+ }
433
+ }
434
+
435
+ const ulStyle = useMenuCssVar(instance.props, 0);
436
+
437
+ const vMenu = h(
438
+ 'ul',
439
+ {
440
+ key: String(collapse),
441
+ role: 'menubar',
442
+ ref: menu,
443
+ style: ulStyle.value,
444
+ class: {
445
+ [ns.b()]: true,
446
+ [ns.m(mode)]: true,
447
+ [ns.m('collapse')]: collapse
448
+ }
449
+ },
450
+ [...slot, ...vShowMore]
451
+ );
452
+
453
+ if (collapseTransition && mode === 'vertical') {
454
+ return h(MenuCollapseTransition, () => vMenu);
455
+ }
456
+
457
+ return vMenu;
458
+ });
459
+ </script>