@pubinfo/core 2.0.0-rc.1 → 2.0.0-rc.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 (101) hide show
  1. package/dist/{AppSetting-Bblni_W7.js → AppSetting-3wJKvibc.js} +19 -19
  2. package/dist/{HCheckList.vue_vue_type_script_setup_true_lang-A7PnhNKK.js → HCheckList.vue_vue_type_script_setup_true_lang-17EywJvs.js} +1 -1
  3. package/dist/{HToggle-xibvPpmf.js → HToggle-B-ZjSh6S.js} +1 -1
  4. package/dist/{PreferencesContent--JtPw_LT.js → PreferencesContent-xT4paU7N.js} +52 -52
  5. package/dist/{SettingBreadcrumb-BwhdAtkC.js → SettingBreadcrumb-CYnO51Ek.js} +5 -5
  6. package/dist/{SettingCopyright-DPsRDagL.js → SettingCopyright-FOW5ObHK.js} +2 -2
  7. package/dist/{SettingEnableTransition-mtHTUb08.js → SettingEnableTransition-Q5cvubmF.js} +12 -12
  8. package/dist/{SettingHome-agxHFIKq.js → SettingHome-Df7-AlWB.js} +8 -8
  9. package/dist/{SettingMenu-D6WwcOw-.js → SettingMenu-BNAJ3el9.js} +14 -14
  10. package/dist/{SettingMode-CkmhszlD.js → SettingMode-LzlRsBL9.js} +3 -3
  11. package/dist/{SettingNavSearch-CEB5DZGf.js → SettingNavSearch-BA08vYuw.js} +6 -6
  12. package/dist/{SettingOther-Dzs1uwnC.js → SettingOther-BE8dDCYD.js} +11 -11
  13. package/dist/{SettingPage-bOThkL2-.js → SettingPage-D061yXCv.js} +2 -2
  14. package/dist/{SettingTabbar-L6yQxAeE.js → SettingTabbar-COwdPPKy.js} +13 -13
  15. package/dist/{SettingThemes-Cxa5bR66.js → SettingThemes-BHaYERNp.js} +12 -12
  16. package/dist/{SettingToolbar-BZ2uHom2.js → SettingToolbar-fSuzu6ND.js} +10 -10
  17. package/dist/{SettingTopbar-hptxTh8m.js → SettingTopbar-D7GgP0KB.js} +6 -6
  18. package/dist/{SettingWidthMode-D2R2W4-n.js → SettingWidthMode-CNjzChe1.js} +11 -11
  19. package/dist/{TopThinMode-C26i10py.js → TopThinMode-B-28sBJD.js} +3 -3
  20. package/dist/built-in/layout-component/Layout.vue.d.ts +1 -0
  21. package/dist/built-in/layout-component/components/Tools/Fullscreen.vue.d.ts +2 -0
  22. package/dist/built-in/layout-component/components/Tools/PageReload.vue.d.ts +2 -0
  23. package/dist/built-in/layout-component/components/Tools/SearchBar.vue.d.ts +2 -0
  24. package/dist/built-in/layout-component/components/Tools/index.vue.d.ts +47 -207
  25. package/dist/built-in/layout-component/components/Tools/interface.d.ts +26 -0
  26. package/dist/built-in/layout-component/components/Topbar/Tabbar/MoreAction.vue.d.ts +0 -206
  27. package/dist/built-in/layout-component/components/ui/HDropdownMenu.vue.d.ts +2 -7
  28. package/dist/built-in/layout-component/composables/useGlobalSearch.d.ts +19 -0
  29. package/dist/built-in/layout-component/composables/useHotkey.d.ts +1 -5
  30. package/dist/built-in/layout-component/composables/useMainPage.d.ts +1 -1
  31. package/dist/built-in/layout-component/composables/useMenu.d.ts +1 -1
  32. package/dist/built-in/layout-component/composables/useTabbar.d.ts +1 -1
  33. package/dist/built-in/layout-component/composables/useTitle.d.ts +2 -1
  34. package/dist/built-in/layout-component/composables/useWatermark.d.ts +3 -1
  35. package/dist/built-in/layout-component/index.d.ts +9 -6
  36. package/dist/built-in/layout-component/utils/index.d.ts +0 -1
  37. package/dist/{colors-C8I6Fdyp.js → colors-BIQSd520.js} +1 -1
  38. package/dist/{index-RwbJwutI.js → index-Beb7_0-E.js} +14499 -14397
  39. package/dist/{index-D948mfZb.js → index-CPRiufg0.js} +1 -1
  40. package/dist/{index-DZ6SbWiv.js → index-DNdH93AP.js} +13 -13
  41. package/dist/index-DYMBkmAp.js +139 -0
  42. package/dist/{index-DdS6qgK_.js → index-Dlf6GQBd.js} +6418 -6439
  43. package/dist/{index-D3zjK9rd.js → index-IscoZG-Y.js} +2 -2
  44. package/dist/{index-DeWCd_-p.js → index-WubcSL0v.js} +1 -1
  45. package/dist/index-YSjb6X1D.js +232 -0
  46. package/dist/{index-EOTrQlGo.js → index-wxEEuQXu.js} +17 -17
  47. package/dist/index.d.ts +4 -2
  48. package/dist/index.js +53 -55
  49. package/dist/{pick-D6j4F3UM.js → pick-D_XPbQHB.js} +1 -1
  50. package/dist/style.css +1 -1
  51. package/package.json +10 -10
  52. package/src/built-in/authentication/pages/change-organization/assets/change-org.svg +1 -1
  53. package/src/built-in/authentication/pages/change-password/assets/mima.svg +1 -1
  54. package/src/built-in/authorization/pages/not-permission/403.svg +1 -1
  55. package/src/built-in/authorization/pages/not-permission/403_dark.svg +1 -1
  56. package/src/built-in/layout-component/Layout.vue +11 -22
  57. package/src/built-in/layout-component/assets/icons/favorites.svg +1 -1
  58. package/src/built-in/layout-component/assets/icons/toolbar-collapse.svg +1 -1
  59. package/src/built-in/layout-component/components/Header/TopMode/index.vue +1 -1
  60. package/src/built-in/layout-component/components/Sidebar/MainSidebar.vue +1 -1
  61. package/src/built-in/layout-component/components/Sidebar/index.vue +1 -1
  62. package/src/built-in/layout-component/components/Tools/DarkModeToggle.vue +108 -0
  63. package/src/built-in/layout-component/components/Tools/Fullscreen.vue +24 -0
  64. package/src/built-in/layout-component/components/Tools/PageReload.vue +22 -0
  65. package/src/built-in/layout-component/components/Tools/SearchBar.vue +44 -0
  66. package/src/built-in/layout-component/components/Tools/{Search.vue → SearchPanel.vue} +12 -6
  67. package/src/built-in/layout-component/components/Tools/index.vue +64 -130
  68. package/src/built-in/layout-component/components/Tools/interface.ts +27 -0
  69. package/src/built-in/layout-component/components/Topbar/Tabbar/MoreAction.vue +7 -6
  70. package/src/built-in/layout-component/components/Topbar/Tabbar/index.vue +2 -2
  71. package/src/built-in/layout-component/components/Topbar/Toolbar/index.vue +32 -10
  72. package/src/built-in/layout-component/components/ui/HDropdownMenu.vue +19 -26
  73. package/src/built-in/layout-component/composables/useGetComputedStyle.ts +2 -3
  74. package/src/built-in/layout-component/composables/useGlobalSearch.ts +48 -0
  75. package/src/built-in/layout-component/composables/useHotkey.ts +6 -10
  76. package/src/built-in/layout-component/composables/useMainPage.ts +5 -6
  77. package/src/built-in/layout-component/composables/useMenu.ts +3 -5
  78. package/src/built-in/layout-component/composables/useTabbar.ts +3 -5
  79. package/src/built-in/layout-component/composables/useTitle.ts +2 -3
  80. package/src/built-in/layout-component/composables/useWatermark.ts +25 -12
  81. package/src/built-in/layout-component/index.ts +21 -12
  82. package/src/built-in/layout-component/provider.ts +6 -2
  83. package/src/built-in/layout-component/utils/index.ts +0 -1
  84. package/src/built-in/settings/router.ts +5 -1
  85. package/src/features/assets/fonts/digital-7/digital-7-webfont.svg +131 -133
  86. package/src/features/assets/fonts/digital-7_mono/digital-7_mono-webfont.svg +130 -132
  87. package/src/features/assets/images/org.svg +2 -2
  88. package/src/features/components/NotAllowed/assets/403.svg +1 -1
  89. package/src/features/components/NotAllowed/assets/403_dark.svg +1 -1
  90. package/src/features/components/PubinfoIcon/index.vue +1 -1
  91. package/src/features/pages/not-found/assets/404.svg +1 -1
  92. package/src/features/pages/not-found/assets/404_dark.svg +1 -1
  93. package/src/features/stores/utils/routerHelper.ts +3 -0
  94. package/src/index.ts +7 -9
  95. package/dist/built-in/layout-component/utils/eventBus.d.ts +0 -5
  96. package/dist/index-CkyHqPON.js +0 -139
  97. package/dist/index-xzfvpJSq.js +0 -234
  98. package/src/built-in/layout-component/components/Tools/DayNightSwitch.vue +0 -70
  99. package/src/built-in/layout-component/utils/eventBus.ts +0 -8
  100. /package/dist/built-in/layout-component/components/Tools/{DayNightSwitch.vue.d.ts → DarkModeToggle.vue.d.ts} +0 -0
  101. /package/dist/built-in/layout-component/components/Tools/{Search.vue.d.ts → SearchPanel.vue.d.ts} +0 -0
@@ -1,128 +1,108 @@
1
1
  <script setup lang="ts">
2
- import { useFullscreen } from '@vueuse/core';
2
+ import type { DropdownMenuRender } from './interface';
3
3
  import { useI18n } from 'vue-i18n';
4
4
  import CarbonUserAvatarFilledAlt from '~icons/carbon/user-avatar-filled-alt';
5
- import FluentArrowSync16Filled from '~icons/fluent/arrow-sync-16-filled';
6
- import FluentFullScreenMaximize16Filled from '~icons/fluent/full-screen-maximize-16-filled';
7
- import FluentFullScreenMinimize16Filled from '~icons/fluent/full-screen-minimize-16-filled';
8
- import IconamoonSearch from '~icons/iconamoon/search';
9
5
  import MaterialSymbolsExpandMoreRounded from '~icons/material-symbols/expand-more-rounded';
10
- import PhLineVerticalThin from '~icons/ph/line-vertical-thin';
11
6
 
7
+ import PhLineVerticalThin from '~icons/ph/line-vertical-thin';
12
8
  import { useContext } from '../../composables/useContext';
13
- import useMainPage from '../../composables/useMainPage';
14
- import useViewTransition from '../../composables/useViewTransition';
15
- import DayNightSwitch from './DayNightSwitch.vue';
9
+ import DarkModeToggle from './DarkModeToggle.vue';
10
+ import Fullscreen from './Fullscreen.vue';
16
11
  import HotkeysIntro from './HotkeysIntro.vue';
12
+ import PageReload from './PageReload.vue';
17
13
  import Preferences from './Preferences/index.vue';
18
- import Search from './Search.vue';
14
+ import SearchBar from './SearchBar.vue';
15
+ import SearchPanel from './SearchPanel.vue';
19
16
 
20
17
  defineOptions({
21
18
  name: 'Tools',
22
19
  });
23
20
 
21
+ const props = withDefaults(
22
+ defineProps<{
23
+ // 用户菜单渲染函数
24
+ dropdownMenuRender?: DropdownMenuRender
25
+ }>(),
26
+ {
27
+ dropdownMenuRender: (({ Home, Preferences, Hotkeys, Org, Profile, Password, Logout, Divider }) => {
28
+ return [Home, Preferences, Divider, Hotkeys, Divider, Org, Profile, Password, Logout];
29
+ }) as DropdownMenuRender,
30
+ },
31
+ );
32
+
24
33
  const router = useRouter();
25
34
  const { settingsStore, userStore, generateI18nTitle } = useContext();
26
35
 
27
36
  const { t } = useI18n();
28
37
 
29
- const mainPage = useMainPage();
30
- const { isFullscreen, toggle } = useFullscreen();
31
-
32
38
  const hotkeysIntroRef = ref();
33
39
  const preferencesRef = ref();
34
- const searchRef = ref();
35
40
 
36
- const userMenu = computed(() => [
37
- [
38
- {
41
+ const userMenu = computed(() => {
42
+ return {
43
+ Home: {
44
+ key: 'home',
39
45
  label: generateI18nTitle('route.home', settingsStore.settings.home.title),
40
46
  icon: 'i-ant-design:home-twotone',
41
- handle: () => router.push({ name: 'Home' }),
47
+ onClick: () => router.push({ name: 'Home' }),
42
48
  hide: !settingsStore.settings.home.enable,
43
49
  },
44
- {
50
+ Preferences: {
51
+ key: 'preferences',
45
52
  label: t('app.preferences'),
46
53
  icon: 'i-iconamoon-star-duotone',
47
- handle: () => preferencesRef.value?.open?.(),
54
+ onClick: () => preferencesRef.value?.open?.(),
48
55
  hide: !settingsStore.settings.app.enableUserPreferences,
49
56
  },
50
- ],
51
- [
52
- {
57
+ Hotkeys: {
58
+ key: 'hotkeys',
53
59
  label: t('app.hotkeys'),
54
60
  icon: 'i-iconamoon-lightning-2-duotone',
55
- handle: () => hotkeysIntroRef.value?.open?.(),
61
+ onClick: () => hotkeysIntroRef.value?.open?.(),
56
62
  hide: settingsStore.mode !== 'pc',
57
63
  },
58
- ],
59
- [
60
- {
64
+ Org: {
65
+ key: 'org',
61
66
  label: t('app.changeOrg'),
62
67
  icon: 'i-iconamoon-synchronize-duotone',
63
- handle: () => router.push({
68
+ onClick: () => router.push({
64
69
  name: 'ChangeOrganization',
65
70
  params: { orgId: userStore.user.orgId },
66
71
  }),
67
72
  hide: userStore.user.orgList.length <= 1,
68
73
  },
69
- {
74
+ Profile: {
75
+ key: 'profile',
70
76
  label: t('route.personal.profile'),
71
77
  icon: 'i-iconamoon-profile-duotone',
72
- handle: () => router.push({ name: 'Profile' }),
78
+ onClick: () => router.push({ name: 'Profile' }),
73
79
  },
74
- {
80
+ Password: {
81
+ key: 'password',
75
82
  label: t('app.changePassword'),
76
83
  icon: 'i-iconamoon-lock-duotone',
77
- handle: () => router.push({
84
+ onClick: () => router.push({
78
85
  name: 'ChangePassword',
79
86
  params: {
80
87
  changePassWordToken: userStore.user.token,
81
88
  },
82
89
  }),
83
90
  },
84
- {
91
+ Logout: {
92
+ key: 'logout',
85
93
  label: t('app.logout'),
86
94
  icon: 'i-iconamoon-arrow-left-3-square-duotone',
87
- handle: () => userStore.logout(),
95
+ onClick: () => userStore.logout(),
88
96
  },
89
- ],
90
- ]);
91
-
92
- function toggleColorScheme(event: MouseEvent) {
93
- const { startViewTransition } = useViewTransition(async () => {
94
- const colorScheme = settingsStore.settings.app.colorScheme === 'dark' ? 'light' : 'dark';
95
- settingsStore.setColorScheme(colorScheme);
96
- await settingsStore.setPreferencesSetting({
97
- app: {
98
- colorScheme,
99
- },
100
- });
101
- });
97
+ Divider: {
98
+ key: 'divider',
99
+ },
100
+ };
101
+ });
102
102
 
103
- startViewTransition()?.ready.then(() => {
104
- const x = event.clientX;
105
- const y = event.clientY;
106
- const endRadius = Math.hypot(
107
- Math.max(x, innerWidth - x),
108
- Math.max(y, innerHeight - y),
109
- );
110
- const clipPath = [
111
- `circle(0px at ${x}px ${y}px)`,
112
- `circle(${endRadius}px at ${x}px ${y}px)`,
113
- ];
114
- document.documentElement.animate(
115
- {
116
- clipPath: settingsStore.settings.app.colorScheme !== 'dark' ? clipPath : clipPath.reverse(),
117
- },
118
- {
119
- duration: 300,
120
- easing: 'ease-out',
121
- pseudoElement: settingsStore.settings.app.colorScheme !== 'dark' ? '::view-transition-new(root)' : '::view-transition-old(root)',
122
- },
123
- );
124
- });
125
- }
103
+ const menus = computed(() => {
104
+ return props.dropdownMenuRender?.(userMenu.value);
105
+ });
126
106
 
127
107
  const avatarError = ref(false);
128
108
 
@@ -131,73 +111,33 @@ watch(() => userStore.user.avatar, () => {
131
111
  avatarError.value = false;
132
112
  }
133
113
  });
134
-
135
- const searchComponentsClass = computed(() => {
136
- const componentsClass = {
137
- 'side': 'ring-1 ',
138
- 'head': 'bg-[var(--g-header-menu-active-bg)]',
139
- 'single': ' ring-1',
140
- 'only-side': 'ring-1',
141
- 'only-head': ' bg-[var(--g-header-menu-active-bg)]',
142
- };
143
- const menuMode = settingsStore.settings.menu.menuMode;
144
- return componentsClass[menuMode];
145
- });
146
114
  </script>
147
115
 
148
116
  <template>
149
117
  <div class="tools flex items-center gap-4 whitespace-nowrap px-4">
150
- <span
151
- v-if="settingsStore.settings.navSearch.enable && settingsStore.mode === 'pc'"
152
- class="group inline-flex cursor-pointer items-center gap-1 rounded-2 px-2 py-1.5 hover:ring-1 ring-inset transition ring-stone-3 dark:ring-stone-7"
153
- :class="searchComponentsClass"
154
- @click="searchRef?.toggle?.()"
155
- >
156
- <IconamoonSearch text="14px" />
157
- <span class="text-sm transition ">{{ t('app.search.text') }}</span>
158
- <HKbd
159
- v-if="settingsStore.settings.navSearch.enableHotkeys"
160
- class="ml-2"
161
- >{{ settingsStore.os === 'mac' ? '⌥' : 'Alt' }} S</HKbd>
162
- </span>
163
- <div class="flex items-center empty:hidden">
164
- <span
165
- v-if="settingsStore.settings.navSearch.enable && settingsStore.mode === 'mobile'"
166
- class="item"
167
- @click="searchRef?.toggle?.()"
168
- >
169
- <IconamoonSearch text="14px" />
170
- </span>
171
- <span
172
- v-if="settingsStore.mode === 'pc' && settingsStore.settings.toolbar.enableFullscreen"
173
- class="item"
174
- @click="toggle"
175
- >
176
- <FluentFullScreenMinimize16Filled v-if="isFullscreen" text="16px" />
177
- <FluentFullScreenMaximize16Filled v-else text="16px" />
178
- </span>
179
- <span
180
- v-if="settingsStore.settings.toolbar.enablePageReload"
181
- class="item"
182
- @click="mainPage.reload()"
183
- >
184
- <FluentArrowSync16Filled text="15px" />
185
- </span>
186
- <DayNightSwitch @click="toggleColorScheme" />
118
+ <SearchBar />
119
+
120
+ <div class="flex items-center empty:hidden gap-3">
121
+ <slot name="default">
122
+ <Fullscreen />
123
+ <PageReload />
124
+ <DarkModeToggle />
125
+ </slot>
187
126
  </div>
127
+
188
128
  <div flex-center cursor-pointer gap-1>
189
129
  <img
190
130
  v-if="userStore.user.avatar && !avatarError"
191
131
  :src="userStore.user.avatar"
192
132
  :onerror="() => (avatarError = true)"
193
- class="h-[24px] w-[24px] rounded-full"
133
+ class="size-[24px] rounded-full"
194
134
  >
195
135
  <CarbonUserAvatarFilledAlt v-else text="20px" mr-2px />
196
- <div flex-center cursor-pointer>
136
+ <div class="flex-center cursor-pointer">
197
137
  {{ userStore.userOrgName }}
198
138
  </div>
199
- <PhLineVerticalThin />
200
- <HDropdownMenu :items="userMenu">
139
+ <PhLineVerticalThin v-if="userStore?.userOrgName" />
140
+ <HDropdownMenu :items="menus">
201
141
  <div flex-center cursor-pointer gap-1>
202
142
  {{ userStore.user.account }}
203
143
  <MaterialSymbolsExpandMoreRounded ml="5px" mr="10px" />
@@ -206,13 +146,7 @@ const searchComponentsClass = computed(() => {
206
146
  </div>
207
147
 
208
148
  <HotkeysIntro ref="hotkeysIntroRef" />
209
- <Search ref="searchRef" />
149
+ <SearchPanel />
210
150
  <Preferences v-if="settingsStore.settings.app.enableUserPreferences" ref="preferencesRef" />
211
151
  </div>
212
152
  </template>
213
-
214
- <style scoped>
215
- .item {
216
- --at-apply: flex px-2 py-1 cursor-pointer;
217
- }
218
- </style>
@@ -0,0 +1,27 @@
1
+ export interface DropdownMenu {
2
+ key?: string | 'divider'
3
+ icon?: string
4
+ label?: string
5
+ disabled?: boolean
6
+ hide?: boolean
7
+ onClick?: () => void
8
+ }
9
+
10
+ export type DropdownMenuRender = (items: {
11
+ /** 主页 */
12
+ Home: DropdownMenu
13
+ /** 个人偏好设置 */
14
+ Preferences: DropdownMenu
15
+ /** 快捷键介绍 */
16
+ Hotkeys: DropdownMenu
17
+ /** 切换组织 */
18
+ Org: DropdownMenu
19
+ /** 个人中心 */
20
+ Profile: DropdownMenu
21
+ /** 修改密码 */
22
+ Password: DropdownMenu
23
+ /** 登出 */
24
+ Logout: DropdownMenu
25
+ /** 分割线 */
26
+ Divider: DropdownMenu
27
+ }) => DropdownMenu[];
@@ -12,8 +12,9 @@ import RiCloseFill from '~icons/ri/close-fill';
12
12
  import RiPushpin2Fill from '~icons/ri/pushpin-2-fill';
13
13
  import { PubinfoIcon } from '@/features/components';
14
14
  import { useContext } from '../../../composables/useContext';
15
- import useTabbar from '../../../composables/useTabbar';
16
- import Search from '../../Tools/Search.vue';
15
+ import { useGlobalSearch } from '../../../composables/useGlobalSearch';
16
+ import { useTabbar } from '../../../composables/useTabbar';
17
+ import Search from '../../Tools/SearchPanel.vue';
17
18
 
18
19
  defineOptions({
19
20
  name: 'TabbarMoreAction',
@@ -30,7 +31,7 @@ const { t } = useI18n();
30
31
  const activedTabId = computed(() => tabbar.getId());
31
32
 
32
33
  const dropdownTabContainerRef = ref();
33
- const searchRef = ref();
34
+ const { toggle: toggleGlobalSearch } = useGlobalSearch();
34
35
 
35
36
  const isDragging = ref(false);
36
37
 
@@ -60,7 +61,7 @@ watch(() => dropdownTabContainerRef.value, (val) => {
60
61
  function actionCommand(command: 'search-tabs' | 'other-side' | 'left-side' | 'right-side') {
61
62
  switch (command) {
62
63
  case 'search-tabs':
63
- searchRef.value?.toggle?.('tab');
64
+ toggleGlobalSearch('tab');
64
65
  break;
65
66
  case 'other-side':
66
67
  tabbar.closeOtherSide();
@@ -142,7 +143,7 @@ function iconName(isActive: boolean, icon: Tabbar.recordRaw['icon'], activeIcon:
142
143
  </template>
143
144
  </HDropdown>
144
145
 
145
- <Search ref="searchRef" />
146
+ <Search />
146
147
  </div>
147
148
  </template>
148
149
 
@@ -153,7 +154,7 @@ function iconName(isActive: boolean, icon: Tabbar.recordRaw['icon'], activeIcon:
153
154
 
154
155
  .tabbar-dropdown .quick-button .button {
155
156
  --at-apply: flex items-center justify-center w-8 h-8 cursor-pointer bg-[--g-bg]
156
- transition-[colors] duration-[0.3s] rounded-[5px] border-[none] [outline:none];
157
+ transition-[colors] duration-[0.3s] rounded-[5px] border-0 [outline:none];
157
158
  }
158
159
 
159
160
  .tabbar-dropdown .quick-button .button:hover:not(:disabled) {
@@ -11,8 +11,8 @@ import RiPushpin2Fill from '~icons/ri/pushpin-2-fill';
11
11
  import { PubinfoIcon } from '@/features/components';
12
12
  import { storage } from '@/utils';
13
13
  import { useContext } from '../../../composables/useContext';
14
- import useMainPage from '../../../composables/useMainPage';
15
- import useTabbar from '../../../composables/useTabbar';
14
+ import { useMainPage } from '../../../composables/useMainPage';
15
+ import { useTabbar } from '../../../composables/useTabbar';
16
16
  import MoreAction from './MoreAction.vue';
17
17
  import '@imengyu/vue3-context-menu/lib/vue3-context-menu.css';
18
18
 
@@ -56,8 +56,10 @@ function createHomeBreadcrumb() {
56
56
  };
57
57
  }
58
58
 
59
+ interface BreadcrumbItemNode { title: string, path: string, mainIndex?: number }
60
+
59
61
  const breadcrumbList = computed(() => {
60
- const breadcrumbList: Array<{ title: string, path: string }> = [];
62
+ const breadcrumbList: BreadcrumbItemNode[] = [];
61
63
  if (settingsStore.settings.home.enable) {
62
64
  breadcrumbList.push(createHomeBreadcrumb());
63
65
  }
@@ -74,7 +76,8 @@ const breadcrumbList = computed(() => {
74
76
 
75
77
  if (menuStore.allMenus[index]?.meta) {
76
78
  breadcrumbList.push({
77
- path: '',
79
+ path: `__MAIN__${index}`,
80
+ mainIndex: index,
78
81
  title: generateI18nTitle(menuStore.allMenus[index].meta?.i18n, menuStore.allMenus[index].meta?.title),
79
82
  });
80
83
  }
@@ -147,21 +150,40 @@ function recursiveTreeMap<T extends { children?: T[] }>(tree: T[], fn: (node: T)
147
150
  * 根据 path 字段读取树中的指定节点
148
151
  */
149
152
  function getCurrentBreadcrumb(path: string) {
153
+ if (path.startsWith('__MAIN__')) {
154
+ const idx = Number(path.replace('__MAIN__', ''));
155
+ const main = menuStore.allMenus[idx];
156
+ if (main) {
157
+ const mapNode = (r: any): any => ({
158
+ title: generateI18nTitle(r.meta?.i18n, r.meta?.title),
159
+ path: r.path,
160
+ children: (r.children || [])
161
+ .filter((c: any) => c.meta?.sidebar !== false)
162
+ .map((c: any) => mapNode(c)),
163
+ });
164
+ return {
165
+ title: generateI18nTitle(main.meta?.i18n, main.meta?.title),
166
+ path,
167
+ children: (main.children || [])
168
+ .filter(c => c.meta?.sidebar !== false)
169
+ .map(c => mapNode(c)),
170
+ };
171
+ }
172
+ return {};
173
+ }
174
+
150
175
  const fn = (node: Record<string, any>) => node.path === path;
151
176
  const list = [...breadcrumbTree.value];
152
177
  let currentNode;
153
-
154
178
  for (const node of list) {
155
179
  if (fn(node)) {
156
180
  currentNode = node;
157
181
  break;
158
182
  }
159
-
160
183
  if (node?.children) {
161
184
  list.push(...node.children);
162
185
  }
163
186
  }
164
-
165
187
  return currentNode ?? {};
166
188
  }
167
189
 
@@ -169,16 +191,16 @@ function getCurrentBreadcrumb(path: string) {
169
191
  * 点击面包屑的下拉树节点
170
192
  */
171
193
  function onSelect(node: RouteRecordRaw, index: number) {
172
- if (!node.path || (index === (breadcrumbList.value.length - 1))) {
194
+ if (!node) {
173
195
  return;
174
196
  }
175
197
 
176
- if (!node) {
198
+ if (index === (breadcrumbList.value.length - 1)) {
177
199
  return;
178
200
  }
179
201
 
180
- if (!node?.children?.length) {
181
- router.push(pathCompile(node?.path));
202
+ if (!node?.children?.length && node.path) {
203
+ router.push(pathCompile(node.path));
182
204
  }
183
205
  }
184
206
  </script>
@@ -218,7 +240,7 @@ function onSelect(node: RouteRecordRaw, index: number) {
218
240
  >
219
241
  <HDropdownTree
220
242
  :item="getCurrentBreadcrumb(item.path)"
221
- :disabled="!item.path || (index === breadcrumbList.length - 1) || getCurrentBreadcrumb(item.path)?.children?.length === 1"
243
+ :disabled="(index === breadcrumbList.length - 1) || getCurrentBreadcrumb(item.path)?.children?.length <= 1"
222
244
  @select="onSelect($event, index)"
223
245
  >
224
246
  <span class="flex items-center gap-0.5">
@@ -1,20 +1,13 @@
1
1
  <script setup lang="ts">
2
+ import type { DropdownMenu } from '../Tools/interface';
2
3
  import { PubinfoIcon } from '@/features/components';
3
4
 
4
5
  const props = defineProps<{
5
- items: {
6
- icon?: string
7
- label: string
8
- disabled?: boolean
9
- hide?: boolean
10
- handle?: () => void
11
- }[][]
6
+ items: DropdownMenu[]
12
7
  }>();
13
8
 
14
9
  const myItems = computed(() => {
15
- return props.items.map((item) => {
16
- return item.filter(v => !v.hide);
17
- }).filter(v => v.length);
10
+ return props.items.filter(item => !item?.hide);
18
11
  });
19
12
  </script>
20
13
 
@@ -27,23 +20,23 @@ const myItems = computed(() => {
27
20
  :auto-hide="false"
28
21
  >
29
22
  <slot />
23
+
30
24
  <template #popper>
31
- <div
32
- v-for="(item, index) in myItems"
33
- :key="index"
34
- class="p-1 dark:bg-[var(--g-container-bg)]"
35
- border-b="~ solid stone-2 dark:stone-7 last:size-0"
36
- >
37
- <button
38
- v-for="(v, i) in item"
39
- :key="i"
40
- :disabled="v.disabled"
41
- class="w-full flex cursor-pointer items-center gap-2 border-size-0 rounded-md bg-inherit px-2 py-1.5 text-sm text-dark disabled:cursor-not-allowed dark:text-white disabled:opacity-50 hover:not-disabled:bg-stone-1 dark:hover:not-disabled:bg-stone-9"
42
- @click="v.handle"
43
- >
44
- <PubinfoIcon v-if="v.icon" :name="v.icon" />
45
- {{ v.label }}
46
- </button>
25
+ <div class="dark:bg-[var(--g-container-bg)] py-1">
26
+ <template v-for="(item, index) in myItems" :key="index">
27
+ <div v-if="item?.key === 'divider'" border-b="~ solid stone-2 dark:stone-7 last:size-0" class="my-1" />
28
+
29
+ <div v-else class="px-1">
30
+ <button
31
+ :disabled="item.disabled"
32
+ class="w-full flex cursor-pointer items-center gap-2 border-size-0 rounded-md bg-inherit px-2 py-1.5 text-sm text-dark disabled:cursor-not-allowed dark:text-white disabled:opacity-50 hover:not-disabled:bg-stone-1 dark:hover:not-disabled:bg-stone-9"
33
+ @click="item?.onClick"
34
+ >
35
+ <PubinfoIcon v-if="item?.icon" :name="item.icon" />
36
+ {{ item?.label }}
37
+ </button>
38
+ </div>
39
+ </template>
47
40
  </div>
48
41
  </template>
49
42
  </VMenu>
@@ -1,4 +1,4 @@
1
- import { useMenuStore, useSettingsStore } from '@/features/stores';
1
+ import { useContext } from './useContext';
2
2
 
3
3
  /**
4
4
  * 获取指定元素的计算样式属性值
@@ -17,8 +17,7 @@ type unit = `${string}px`;
17
17
  * @returns 包含主侧边栏和子侧边栏实际宽度的计算属性对象。
18
18
  */
19
19
  export function useGetSidebarActualWidth() {
20
- const settingsStore = useSettingsStore();
21
- const menuStore = useMenuStore();
20
+ const { settingsStore, menuStore } = useContext();
22
21
 
23
22
  const mainSidebarActualWidth = computed<unit>(() => {
24
23
  const menuMode = settingsStore.settings.menu.menuMode;
@@ -0,0 +1,48 @@
1
+ import { ref } from 'vue';
2
+
3
+ type SearchType = 'menu' | 'tab';
4
+
5
+ const isShow = ref(false);
6
+ const searchType = ref<SearchType>('menu');
7
+
8
+ function open(type?: SearchType) {
9
+ if (type) {
10
+ searchType.value = type;
11
+ }
12
+ isShow.value = true;
13
+ }
14
+
15
+ function close() {
16
+ isShow.value = false;
17
+ }
18
+
19
+ function toggle(type?: SearchType) {
20
+ if (!isShow.value) {
21
+ if (type) {
22
+ searchType.value = type;
23
+ }
24
+ isShow.value = true;
25
+ }
26
+ else {
27
+ isShow.value = false;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * 全局搜索服务
33
+ * 可以支持多处挂载,只需要使用 `useGlobalSearch` 开启即可
34
+ * 这样可以避免如果使用快捷键可能会多处开启 search 的问题
35
+ *
36
+ * close [#38](https://github.com/wsypower/pubinfo/issues/38)
37
+ */
38
+ export function useGlobalSearch() {
39
+ return {
40
+ isShow,
41
+ searchType,
42
+ open,
43
+ close,
44
+ toggle,
45
+ };
46
+ }
47
+
48
+ export type { SearchType };
@@ -1,15 +1,11 @@
1
- import type { useMenuStore, useSettingsStore } from '@/features/stores';
2
1
  import hotkeys from 'hotkeys-js';
3
- import useMainPage from './useMainPage';
4
- import useMenu from './useMenu';
2
+ import { useContext } from './useContext';
3
+ import { useMainPage } from './useMainPage';
4
+ import { useMenu } from './useMenu';
5
+
6
+ export function useHotkey() {
7
+ const { settingsStore, menuStore } = useContext();
5
8
 
6
- export function useHotkey({
7
- menuStore,
8
- settingsStore,
9
- }: {
10
- menuStore: ReturnType<typeof useMenuStore>
11
- settingsStore: ReturnType<typeof useSettingsStore>
12
- }) {
13
9
  const mainPage = useMainPage();
14
10
  const menu = useMenu();
15
11
 
@@ -1,5 +1,5 @@
1
- import { useSettingsStore, useTabbarStore } from '@/features/stores';
2
- import useTabbar from './useTabbar';
1
+ import { useContext } from './useContext';
2
+ import { useTabbar } from './useTabbar';
3
3
 
4
4
  /**
5
5
  * 用于主页面功能的自定义钩子。
@@ -9,13 +9,12 @@ import useTabbar from './useTabbar';
9
9
  * - resetCustomTitle: 重置当前页面的自定义标题。
10
10
  * - maximize: 设置主页面的最大化状态。
11
11
  */
12
- export default function useMainPage() {
12
+ export function useMainPage() {
13
+ const { settingsStore, tabbarStore } = useContext();
14
+
13
15
  const route = useRoute();
14
16
  const router = useRouter();
15
17
 
16
- const settingsStore = useSettingsStore();
17
- const tabbarStore = useTabbarStore();
18
-
19
18
  const tabbar = useTabbar();
20
19
 
21
20
  /**
@@ -1,17 +1,15 @@
1
1
  import type { Menu } from '#/menu';
2
2
  import { message } from 'ant-design-vue';
3
- import { useMenuStore, useSettingsStore } from '@/features/stores';
3
+ import { useContext } from './useContext';
4
4
 
5
5
  /**
6
6
  * 用于管理菜单功能的自定义组合函数。
7
7
  * @returns 一个包含 `switchTo` 函数的对象。
8
8
  */
9
- export default function useMenu() {
9
+ export function useMenu() {
10
+ const { settingsStore, menuStore } = useContext();
10
11
  const router = useRouter();
11
12
 
12
- const settingsStore = useSettingsStore();
13
- const menuStore = useMenuStore();
14
-
15
13
  function switchTo(index: number | string, mainRouter?: Menu.recordMainRaw) {
16
14
  if (mainRouter && mainRouter.meta?.isDev) {
17
15
  message.info('暂未上线,敬请期待!');