@pubinfo/core 2.0.0-rc.2 → 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 (81) hide show
  1. package/dist/{AppSetting-D2RJrc9O.js → AppSetting-3wJKvibc.js} +19 -19
  2. package/dist/{HCheckList.vue_vue_type_script_setup_true_lang-DusVz35O.js → HCheckList.vue_vue_type_script_setup_true_lang-17EywJvs.js} +1 -1
  3. package/dist/{HToggle-DMcVgMVY.js → HToggle-B-ZjSh6S.js} +1 -1
  4. package/dist/{PreferencesContent-Dtd9rtew.js → PreferencesContent-xT4paU7N.js} +52 -52
  5. package/dist/{SettingBreadcrumb-QSCSviKM.js → SettingBreadcrumb-CYnO51Ek.js} +5 -5
  6. package/dist/{SettingCopyright-Dr5P6yfq.js → SettingCopyright-FOW5ObHK.js} +2 -2
  7. package/dist/{SettingEnableTransition-DGiHEbCI.js → SettingEnableTransition-Q5cvubmF.js} +12 -12
  8. package/dist/{SettingHome-CEPcBlds.js → SettingHome-Df7-AlWB.js} +8 -8
  9. package/dist/{SettingMenu-BJdjnRA6.js → SettingMenu-BNAJ3el9.js} +14 -14
  10. package/dist/{SettingMode-BnuCHoEY.js → SettingMode-LzlRsBL9.js} +3 -3
  11. package/dist/{SettingNavSearch-CiU4BmlU.js → SettingNavSearch-BA08vYuw.js} +6 -6
  12. package/dist/{SettingOther-DTHjVlFe.js → SettingOther-BE8dDCYD.js} +11 -11
  13. package/dist/{SettingPage-D75_Nf05.js → SettingPage-D061yXCv.js} +2 -2
  14. package/dist/{SettingTabbar-D48dzvgA.js → SettingTabbar-COwdPPKy.js} +13 -13
  15. package/dist/{SettingThemes-D-8vTs5n.js → SettingThemes-BHaYERNp.js} +12 -12
  16. package/dist/{SettingToolbar-DjIjm9V-.js → SettingToolbar-fSuzu6ND.js} +10 -10
  17. package/dist/{SettingTopbar-Cg30OTH3.js → SettingTopbar-D7GgP0KB.js} +6 -6
  18. package/dist/{SettingWidthMode-BKV_7kb8.js → SettingWidthMode-CNjzChe1.js} +11 -11
  19. package/dist/{TopThinMode-JFYsp_lJ.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 -1
  25. package/dist/built-in/layout-component/components/Tools/interface.d.ts +26 -0
  26. package/dist/built-in/layout-component/components/ui/HDropdownMenu.vue.d.ts +2 -7
  27. package/dist/built-in/layout-component/composables/useHotkey.d.ts +1 -5
  28. package/dist/built-in/layout-component/composables/useMainPage.d.ts +1 -1
  29. package/dist/built-in/layout-component/composables/useMenu.d.ts +1 -1
  30. package/dist/built-in/layout-component/composables/useTabbar.d.ts +1 -1
  31. package/dist/built-in/layout-component/composables/useTitle.d.ts +2 -1
  32. package/dist/built-in/layout-component/composables/useWatermark.d.ts +3 -1
  33. package/dist/built-in/layout-component/index.d.ts +9 -6
  34. package/dist/built-in/layout-component/utils/index.d.ts +0 -1
  35. package/dist/{colors-CODcBxrF.js → colors-BIQSd520.js} +1 -1
  36. package/dist/{index-Jd3PYkpj.js → index-Beb7_0-E.js} +11384 -11329
  37. package/dist/{index-BAoB7aoj.js → index-CPRiufg0.js} +1 -1
  38. package/dist/{index-DmcblkoZ.js → index-DNdH93AP.js} +13 -13
  39. package/dist/{index-DQn1WFMa.js → index-DYMBkmAp.js} +2 -2
  40. package/dist/{index-BVLkBCRY.js → index-Dlf6GQBd.js} +6418 -6439
  41. package/dist/{index-DvJr0paY.js → index-IscoZG-Y.js} +2 -2
  42. package/dist/{index-BROqFYXS.js → index-WubcSL0v.js} +1 -1
  43. package/dist/{index-RT-QBzm0.js → index-YSjb6X1D.js} +10 -12
  44. package/dist/{index-D4_xmL_A.js → index-wxEEuQXu.js} +17 -17
  45. package/dist/index.d.ts +4 -2
  46. package/dist/index.js +53 -55
  47. package/dist/{pick-BLJM77QN.js → pick-D_XPbQHB.js} +1 -1
  48. package/dist/style.css +1 -1
  49. package/package.json +9 -9
  50. package/src/built-in/layout-component/Layout.vue +11 -22
  51. package/src/built-in/layout-component/components/Header/TopMode/index.vue +1 -1
  52. package/src/built-in/layout-component/components/Sidebar/MainSidebar.vue +1 -1
  53. package/src/built-in/layout-component/components/Sidebar/index.vue +1 -1
  54. package/src/built-in/layout-component/components/Tools/DarkModeToggle.vue +108 -0
  55. package/src/built-in/layout-component/components/Tools/Fullscreen.vue +24 -0
  56. package/src/built-in/layout-component/components/Tools/PageReload.vue +22 -0
  57. package/src/built-in/layout-component/components/Tools/SearchBar.vue +44 -0
  58. package/src/built-in/layout-component/components/Tools/{Search.vue → SearchPanel.vue} +1 -1
  59. package/src/built-in/layout-component/components/Tools/index.vue +63 -131
  60. package/src/built-in/layout-component/components/Tools/interface.ts +27 -0
  61. package/src/built-in/layout-component/components/Topbar/Tabbar/MoreAction.vue +2 -2
  62. package/src/built-in/layout-component/components/Topbar/Tabbar/index.vue +2 -2
  63. package/src/built-in/layout-component/components/ui/HDropdownMenu.vue +19 -26
  64. package/src/built-in/layout-component/composables/useGetComputedStyle.ts +2 -3
  65. package/src/built-in/layout-component/composables/useHotkey.ts +6 -10
  66. package/src/built-in/layout-component/composables/useMainPage.ts +5 -6
  67. package/src/built-in/layout-component/composables/useMenu.ts +3 -5
  68. package/src/built-in/layout-component/composables/useTabbar.ts +3 -5
  69. package/src/built-in/layout-component/composables/useTitle.ts +2 -3
  70. package/src/built-in/layout-component/composables/useWatermark.ts +25 -12
  71. package/src/built-in/layout-component/index.ts +21 -12
  72. package/src/built-in/layout-component/provider.ts +6 -2
  73. package/src/built-in/layout-component/utils/index.ts +0 -1
  74. package/src/built-in/settings/router.ts +5 -1
  75. package/src/features/stores/utils/routerHelper.ts +3 -0
  76. package/src/index.ts +7 -9
  77. package/dist/built-in/layout-component/utils/eventBus.d.ts +0 -5
  78. package/src/built-in/layout-component/components/Tools/DayNightSwitch.vue +0 -70
  79. package/src/built-in/layout-component/utils/eventBus.ts +0 -8
  80. /package/dist/built-in/layout-component/components/Tools/{DayNightSwitch.vue.d.ts → DarkModeToggle.vue.d.ts} +0 -0
  81. /package/dist/built-in/layout-component/components/Tools/{Search.vue.d.ts → SearchPanel.vue.d.ts} +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pubinfo/core",
3
3
  "type": "module",
4
- "version": "2.0.0-rc.2",
4
+ "version": "2.0.0-rc.3",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.ts",
@@ -24,15 +24,15 @@
24
24
  "vue": "^3.5.17",
25
25
  "vue-i18n": "^10.0.7",
26
26
  "vue-router": "^4.5.1",
27
- "@pubinfo/config": "2.0.0-rc.2",
28
- "@pubinfo/vite": "2.0.0-rc.2"
27
+ "@pubinfo/config": "2.0.0-rc.3",
28
+ "@pubinfo/vite": "2.0.0-rc.3"
29
29
  },
30
30
  "dependencies": {
31
31
  "@alova/adapter-axios": "^2.0.16",
32
32
  "@ant-design/icons-vue": "^7.0.1",
33
33
  "@headlessui/vue": "^1.7.23",
34
34
  "@imengyu/vue3-context-menu": "^1.5.1",
35
- "@pubinfo/pro-components": "^1.7.1",
35
+ "@pubinfo/pro-components": "^1.7.3",
36
36
  "@unocss/reset": "^66.4.2",
37
37
  "@vueuse/core": "^13.7.0",
38
38
  "@vueuse/integrations": "^13.5.0",
@@ -60,8 +60,8 @@
60
60
  "devDependencies": {
61
61
  "@iconify/json": "^2.2.354",
62
62
  "@iconify/vue": "^5.0.0",
63
- "@pubinfo/openapi": "^0.8.4",
64
- "@pubinfo/preset-openapi": "^0.8.4",
63
+ "@pubinfo/openapi": "^0.9.0",
64
+ "@pubinfo/preset-openapi": "^0.9.0",
65
65
  "@types/lodash-es": "^4.17.12",
66
66
  "@types/md5": "^2.3.5",
67
67
  "@types/nprogress": "^0.2.3",
@@ -82,11 +82,11 @@
82
82
  "vue": "^3.5.17",
83
83
  "vue-i18n": "^10.0.7",
84
84
  "vue-router": "^4.5.1",
85
- "@pubinfo/config": "2.0.0-rc.2",
86
- "@pubinfo/vite": "2.0.0-rc.2"
85
+ "@pubinfo/config": "2.0.0-rc.3",
86
+ "@pubinfo/vite": "2.0.0-rc.3"
87
87
  },
88
88
  "scripts": {
89
- "dev": "vite build -w -m watch --sourcemap",
89
+ "dev": "vite build -w -m watch",
90
90
  "build": "vite build",
91
91
  "openapi": "pnpx @pubinfo/openapi generate"
92
92
  }
@@ -4,30 +4,23 @@ import BackTop from './components/BackTop/index.vue';
4
4
  import LayoutContent from './components/Content/index.vue';
5
5
  import Copyright from './components/Copyright/index.vue';
6
6
  import LayoutHeader from './components/Header/index.vue';
7
-
8
7
  import SettingBar from './components/SettingBar/index.vue';
9
- // import Sidebar from './components/Sidebar/index.vue';
10
- import MainSidebar from './components/Sidebar/MainSidebar.vue';
11
- import SubSidebar from './components/Sidebar/SubSidebar.vue';
12
- import Topbar from './components/Topbar/index.vue';
8
+ import LayoutSidebar from './components/Sidebar/index.vue';
9
+ import LayoutTopbar from './components/Topbar/index.vue';
10
+
13
11
  import { useContext } from './composables/useContext';
14
12
  import { useGetSidebarActualWidth } from './composables/useGetComputedStyle';
15
13
  import { useHotkey } from './composables/useHotkey';
16
- import useWatermark from './composables/useWatermark';
14
+ import { useWatermark } from './composables/useWatermark';
17
15
 
18
16
  defineOptions({
19
17
  name: 'Layout',
20
18
  });
21
19
 
22
20
  const routeInfo = useRoute();
23
- const { settingsStore, menuStore } = useContext();
21
+ const { settingsStore } = useContext();
24
22
  const { mainSidebarActualWidth, subSidebarActualWidth } = useGetSidebarActualWidth();
25
-
26
- useHotkey({
27
- settingsStore,
28
- menuStore,
29
- });
30
-
23
+ useHotkey();
31
24
  useWatermark();
32
25
 
33
26
  watch(() => settingsStore.settings.menu.subMenuCollapse, (val) => {
@@ -68,13 +61,9 @@ watch(() => routeInfo.path, () => {
68
61
  class="sidebar-container"
69
62
  :class="{ show: settingsStore.mode === 'mobile' && !settingsStore.settings.menu.subMenuCollapse }"
70
63
  >
71
- <!-- TODO 这种写法会导致侧边栏模式的布局下,有样式问题 -->
72
- <!-- <slot name="sidebar">
73
- <Sidebar />
74
- </slot> -->
75
-
76
- <MainSidebar />
77
- <SubSidebar />
64
+ <slot name="sidebar">
65
+ <LayoutSidebar />
66
+ </slot>
78
67
  </div>
79
68
 
80
69
  <div
@@ -85,7 +74,7 @@ watch(() => routeInfo.path, () => {
85
74
 
86
75
  <div class="main-container" :style="{ 'padding-bottom': routeInfo.meta.paddingBottom }">
87
76
  <slot name="topbar">
88
- <Topbar />
77
+ <LayoutTopbar />
89
78
  </slot>
90
79
 
91
80
  <div class="main">
@@ -213,7 +202,7 @@ watch(() => routeInfo.path, () => {
213
202
  --at-apply: visible op-100;
214
203
  }
215
204
 
216
- .wrapper .main-sidebar-container:not(.main-sidebar-leave-active) + .sub-sidebar-container {
205
+ .wrapper ::v-deep(.main-sidebar-container:not(.main-sidebar-leave-active) + .sub-sidebar-container) {
217
206
  --at-apply: left-[--g-main-sidebar-width];
218
207
  }
219
208
 
@@ -4,7 +4,7 @@ import { useElementSize } from '@vueuse/core';
4
4
  import SolarWidget5BoldDuotone from '~icons/solar/widget-5-bold-duotone';
5
5
  import { PubinfoIcon } from '@/features/components';
6
6
  import { useContext } from '../../../composables/useContext';
7
- import useMenu from '../../../composables/useMenu';
7
+ import { useMenu } from '../../../composables/useMenu';
8
8
  import More from './More.vue';
9
9
  import NotCursor from './NotCursor.vue';
10
10
 
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { PubinfoIcon } from '@/features/components';
3
3
  import { useContext } from '../../composables/useContext';
4
- import useMenu from '../../composables/useMenu';
4
+ import { useMenu } from '../../composables/useMenu';
5
5
  import Logo from '../Logo/index.vue';
6
6
  import Menu from '../Menu/index.vue';
7
7
 
@@ -3,7 +3,7 @@ import MainSidebar from './MainSidebar.vue';
3
3
  import SubSidebar from './SubSidebar.vue';
4
4
 
5
5
  defineOptions({
6
- name: 'Sidebar',
6
+ name: 'LayoutSidebar',
7
7
  });
8
8
  </script>
9
9
 
@@ -0,0 +1,108 @@
1
+ <script setup lang="ts">
2
+ import { useContext } from '../../composables/useContext';
3
+ import useViewTransition from '../../composables/useViewTransition';
4
+
5
+ defineOptions({
6
+ name: 'DarkModeToggle',
7
+ });
8
+
9
+ const { settingsStore } = useContext();
10
+
11
+ function toggleColorScheme(event: MouseEvent) {
12
+ const { startViewTransition } = useViewTransition(async () => {
13
+ const colorScheme = settingsStore.settings.app.colorScheme === 'dark' ? 'light' : 'dark';
14
+ settingsStore.setColorScheme(colorScheme);
15
+ await settingsStore.setPreferencesSetting({
16
+ app: {
17
+ colorScheme,
18
+ },
19
+ });
20
+ });
21
+
22
+ nextTick(() => {
23
+ startViewTransition()?.ready.then(() => {
24
+ const x = event.clientX;
25
+ const y = event.clientY;
26
+ const endRadius = Math.hypot(
27
+ Math.max(x, innerWidth - x),
28
+ Math.max(y, innerHeight - y),
29
+ );
30
+ const clipPath = [
31
+ `circle(0px at ${x}px ${y}px)`,
32
+ `circle(${endRadius}px at ${x}px ${y}px)`,
33
+ ];
34
+ document.documentElement.animate(
35
+ {
36
+ clipPath: settingsStore.settings.app.colorScheme !== 'dark' ? clipPath : clipPath.reverse(),
37
+ },
38
+ {
39
+ duration: 300,
40
+ easing: 'ease-out',
41
+ pseudoElement: settingsStore.settings.app.colorScheme !== 'dark' ? '::view-transition-new(root)' : '::view-transition-old(root)',
42
+ },
43
+ );
44
+ });
45
+ });
46
+ }
47
+ </script>
48
+
49
+ <template>
50
+ <div
51
+ v-if="settingsStore.settings.toolbar.enableColorScheme"
52
+ class="inline-flex items-center justify-center cursor-pointer"
53
+ @click="toggleColorScheme"
54
+ >
55
+ <div
56
+ class="animate-icon"
57
+ :class="{
58
+ 'animate-icon-day': settingsStore.settings.app.colorScheme !== 'dark',
59
+ }"
60
+ />
61
+ </div>
62
+ </template>
63
+
64
+ <style scoped>
65
+ .animate-icon {
66
+ --at-apply: relative size-[20px] rounded-1/2 inline-block align-middle;
67
+ box-shadow: inset 6px -6px 0 0 #fff; /* 比例缩小 */
68
+ transition: box-shadow 0.5s ease 0s, transform 0.4s ease 0.1s;
69
+ transform: scale(1) rotate(-2deg);
70
+ }
71
+
72
+ .animate-icon::before {
73
+ --at-apply: absolute size-[20px] content-empty rounded-[inherit] left-0 top-0 block;
74
+ transition: background 0.3s ease;
75
+ }
76
+
77
+ .animate-icon::after {
78
+ --at-apply: absolute top-1/2 left-1/2 size-1 -ml-0.5 -mt-0.5 rounded-[50%] content-empty block;
79
+ box-shadow:
80
+ 0 -12px 0 #ffce6e,
81
+ 0 12px 0 #ffce6e,
82
+ 12px 0 0 #ffce6e,
83
+ -12px 0 0 #ffce6e,
84
+ 8px 8px 0 #ffce6e,
85
+ -8px 8px 0 #ffce6e,
86
+ 8px -8px 0 #ffce6e,
87
+ -8px -8px 0 #ffce6e;
88
+ transition: all 0.3s ease;
89
+ transform: scale(0);
90
+ }
91
+
92
+ .animate-icon-day {
93
+ --at-apply: size-5;
94
+ box-shadow: inset 10px -10px 0 0 #fff; /* 缩小后的太阳核心 */
95
+ transition: transform 0.3s ease 0.1s, box-shadow 0.2s ease 0s;
96
+ transform: scale(0.5) rotate(0deg);
97
+ }
98
+
99
+ .animate-icon-day::before {
100
+ background: #ffce6e;
101
+ transition: background 0.3s ease 0.1s;
102
+ }
103
+
104
+ .animate-icon-day::after {
105
+ transition: transform 0.5s ease 0.15s;
106
+ transform: scale(1.2);
107
+ }
108
+ </style>
@@ -0,0 +1,24 @@
1
+ <script setup lang="ts">
2
+ import { useFullscreen } from '@vueuse/core';
3
+ import FluentFullScreenMaximize16Filled from '~icons/fluent/full-screen-maximize-16-filled';
4
+ import FluentFullScreenMinimize16Filled from '~icons/fluent/full-screen-minimize-16-filled';
5
+ import { useContext } from '../../composables/useContext';
6
+
7
+ defineOptions({
8
+ name: 'Fullscreen',
9
+ });
10
+
11
+ const { isFullscreen, toggle } = useFullscreen();
12
+ const { settingsStore } = useContext();
13
+ </script>
14
+
15
+ <template>
16
+ <div
17
+ v-if="settingsStore.mode === 'pc' && settingsStore.settings.toolbar.enableFullscreen"
18
+ class="size-5 cursor-pointer"
19
+ @click="toggle"
20
+ >
21
+ <FluentFullScreenMinimize16Filled v-if="isFullscreen" text="16px" />
22
+ <FluentFullScreenMaximize16Filled v-else text="16px" />
23
+ </div>
24
+ </template>
@@ -0,0 +1,22 @@
1
+ <script setup lang="ts">
2
+ import FluentArrowSync16Filled from '~icons/fluent/arrow-sync-16-filled';
3
+ import { useContext } from '../../composables/useContext';
4
+ import { useMainPage } from '../../composables/useMainPage';
5
+
6
+ defineOptions({
7
+ name: 'PageReload',
8
+ });
9
+
10
+ const { settingsStore } = useContext();
11
+ const mainPage = useMainPage();
12
+ </script>
13
+
14
+ <template>
15
+ <div
16
+ v-if="settingsStore.settings.toolbar.enablePageReload"
17
+ class="size-5 cursor-pointer"
18
+ @click="mainPage.reload()"
19
+ >
20
+ <FluentArrowSync16Filled text="16px" />
21
+ </div>
22
+ </template>
@@ -0,0 +1,44 @@
1
+ <script setup lang="ts">
2
+ import { useI18n } from 'vue-i18n';
3
+ import IconamoonSearch from '~icons/iconamoon/search';
4
+ import { useContext } from '../../composables/useContext';
5
+ import { useGlobalSearch } from '../../composables/useGlobalSearch';
6
+
7
+ defineOptions({
8
+ name: 'SearchBar',
9
+ });
10
+
11
+ const { settingsStore } = useContext();
12
+ const { toggle: toggleSearch } = useGlobalSearch();
13
+ const { t } = useI18n();
14
+
15
+ const searchComponentsClass = computed(() => {
16
+ const componentsClass = {
17
+ 'side': 'ring-1 ',
18
+ 'head': 'bg-[var(--g-header-menu-active-bg)]',
19
+ 'single': ' ring-1',
20
+ 'only-side': 'ring-1',
21
+ 'only-head': ' bg-[var(--g-header-menu-active-bg)]',
22
+ };
23
+ const menuMode = settingsStore.settings.menu.menuMode;
24
+ return componentsClass[menuMode];
25
+ });
26
+ </script>
27
+
28
+ <template>
29
+ <span
30
+ v-if="settingsStore.settings.navSearch.enable"
31
+ 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"
32
+ :class="searchComponentsClass"
33
+ @click="toggleSearch('menu')"
34
+ >
35
+ <IconamoonSearch text="14px" />
36
+ <span class="text-sm transition ">{{ t('app.search.text') }}</span>
37
+ <HKbd
38
+ v-if="settingsStore.settings.navSearch.enableHotkeys"
39
+ class="ml-2"
40
+ >
41
+ {{ settingsStore.os === 'mac' ? '⌥' : 'Alt' }} S
42
+ </HKbd>
43
+ </span>
44
+ </template>
@@ -22,7 +22,7 @@ import Breadcrumb from './Breadcrumb/index.vue';
22
22
  import BreadcrumbItem from './Breadcrumb/item.vue';
23
23
 
24
24
  defineOptions({
25
- name: 'ToolsSearch',
25
+ name: 'SearchPanel',
26
26
  });
27
27
 
28
28
  const overlayTransitionClass = ref({
@@ -1,130 +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 { useGlobalSearch } from '../../composables/useGlobalSearch';
14
- import useMainPage from '../../composables/useMainPage';
15
- import useViewTransition from '../../composables/useViewTransition';
16
- import DayNightSwitch from './DayNightSwitch.vue';
9
+ import DarkModeToggle from './DarkModeToggle.vue';
10
+ import Fullscreen from './Fullscreen.vue';
17
11
  import HotkeysIntro from './HotkeysIntro.vue';
12
+ import PageReload from './PageReload.vue';
18
13
  import Preferences from './Preferences/index.vue';
19
- import Search from './Search.vue';
14
+ import SearchBar from './SearchBar.vue';
15
+ import SearchPanel from './SearchPanel.vue';
20
16
 
21
17
  defineOptions({
22
18
  name: 'Tools',
23
19
  });
24
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
+
25
33
  const router = useRouter();
26
34
  const { settingsStore, userStore, generateI18nTitle } = useContext();
27
35
 
28
36
  const { t } = useI18n();
29
37
 
30
- const mainPage = useMainPage();
31
- const { isFullscreen, toggle } = useFullscreen();
32
-
33
- const { toggle: toggleSearch } = useGlobalSearch();
34
-
35
38
  const hotkeysIntroRef = ref();
36
39
  const preferencesRef = ref();
37
40
 
38
- const userMenu = computed(() => [
39
- [
40
- {
41
+ const userMenu = computed(() => {
42
+ return {
43
+ Home: {
44
+ key: 'home',
41
45
  label: generateI18nTitle('route.home', settingsStore.settings.home.title),
42
46
  icon: 'i-ant-design:home-twotone',
43
- handle: () => router.push({ name: 'Home' }),
47
+ onClick: () => router.push({ name: 'Home' }),
44
48
  hide: !settingsStore.settings.home.enable,
45
49
  },
46
- {
50
+ Preferences: {
51
+ key: 'preferences',
47
52
  label: t('app.preferences'),
48
53
  icon: 'i-iconamoon-star-duotone',
49
- handle: () => preferencesRef.value?.open?.(),
54
+ onClick: () => preferencesRef.value?.open?.(),
50
55
  hide: !settingsStore.settings.app.enableUserPreferences,
51
56
  },
52
- ],
53
- [
54
- {
57
+ Hotkeys: {
58
+ key: 'hotkeys',
55
59
  label: t('app.hotkeys'),
56
60
  icon: 'i-iconamoon-lightning-2-duotone',
57
- handle: () => hotkeysIntroRef.value?.open?.(),
61
+ onClick: () => hotkeysIntroRef.value?.open?.(),
58
62
  hide: settingsStore.mode !== 'pc',
59
63
  },
60
- ],
61
- [
62
- {
64
+ Org: {
65
+ key: 'org',
63
66
  label: t('app.changeOrg'),
64
67
  icon: 'i-iconamoon-synchronize-duotone',
65
- handle: () => router.push({
68
+ onClick: () => router.push({
66
69
  name: 'ChangeOrganization',
67
70
  params: { orgId: userStore.user.orgId },
68
71
  }),
69
72
  hide: userStore.user.orgList.length <= 1,
70
73
  },
71
- {
74
+ Profile: {
75
+ key: 'profile',
72
76
  label: t('route.personal.profile'),
73
77
  icon: 'i-iconamoon-profile-duotone',
74
- handle: () => router.push({ name: 'Profile' }),
78
+ onClick: () => router.push({ name: 'Profile' }),
75
79
  },
76
- {
80
+ Password: {
81
+ key: 'password',
77
82
  label: t('app.changePassword'),
78
83
  icon: 'i-iconamoon-lock-duotone',
79
- handle: () => router.push({
84
+ onClick: () => router.push({
80
85
  name: 'ChangePassword',
81
86
  params: {
82
87
  changePassWordToken: userStore.user.token,
83
88
  },
84
89
  }),
85
90
  },
86
- {
91
+ Logout: {
92
+ key: 'logout',
87
93
  label: t('app.logout'),
88
94
  icon: 'i-iconamoon-arrow-left-3-square-duotone',
89
- handle: () => userStore.logout(),
95
+ onClick: () => userStore.logout(),
90
96
  },
91
- ],
92
- ]);
93
-
94
- function toggleColorScheme(event: MouseEvent) {
95
- const { startViewTransition } = useViewTransition(async () => {
96
- const colorScheme = settingsStore.settings.app.colorScheme === 'dark' ? 'light' : 'dark';
97
- settingsStore.setColorScheme(colorScheme);
98
- await settingsStore.setPreferencesSetting({
99
- app: {
100
- colorScheme,
101
- },
102
- });
103
- });
97
+ Divider: {
98
+ key: 'divider',
99
+ },
100
+ };
101
+ });
104
102
 
105
- startViewTransition()?.ready.then(() => {
106
- const x = event.clientX;
107
- const y = event.clientY;
108
- const endRadius = Math.hypot(
109
- Math.max(x, innerWidth - x),
110
- Math.max(y, innerHeight - y),
111
- );
112
- const clipPath = [
113
- `circle(0px at ${x}px ${y}px)`,
114
- `circle(${endRadius}px at ${x}px ${y}px)`,
115
- ];
116
- document.documentElement.animate(
117
- {
118
- clipPath: settingsStore.settings.app.colorScheme !== 'dark' ? clipPath : clipPath.reverse(),
119
- },
120
- {
121
- duration: 300,
122
- easing: 'ease-out',
123
- pseudoElement: settingsStore.settings.app.colorScheme !== 'dark' ? '::view-transition-new(root)' : '::view-transition-old(root)',
124
- },
125
- );
126
- });
127
- }
103
+ const menus = computed(() => {
104
+ return props.dropdownMenuRender?.(userMenu.value);
105
+ });
128
106
 
129
107
  const avatarError = ref(false);
130
108
 
@@ -133,73 +111,33 @@ watch(() => userStore.user.avatar, () => {
133
111
  avatarError.value = false;
134
112
  }
135
113
  });
136
-
137
- const searchComponentsClass = computed(() => {
138
- const componentsClass = {
139
- 'side': 'ring-1 ',
140
- 'head': 'bg-[var(--g-header-menu-active-bg)]',
141
- 'single': ' ring-1',
142
- 'only-side': 'ring-1',
143
- 'only-head': ' bg-[var(--g-header-menu-active-bg)]',
144
- };
145
- const menuMode = settingsStore.settings.menu.menuMode;
146
- return componentsClass[menuMode];
147
- });
148
114
  </script>
149
115
 
150
116
  <template>
151
117
  <div class="tools flex items-center gap-4 whitespace-nowrap px-4">
152
- <span
153
- v-if="settingsStore.settings.navSearch.enable && settingsStore.mode === 'pc'"
154
- 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"
155
- :class="searchComponentsClass"
156
- @click="toggleSearch('menu')"
157
- >
158
- <IconamoonSearch text="14px" />
159
- <span class="text-sm transition ">{{ t('app.search.text') }}</span>
160
- <HKbd
161
- v-if="settingsStore.settings.navSearch.enableHotkeys"
162
- class="ml-2"
163
- >{{ settingsStore.os === 'mac' ? '⌥' : 'Alt' }} S</HKbd>
164
- </span>
165
- <div class="flex items-center empty:hidden">
166
- <span
167
- v-if="settingsStore.settings.navSearch.enable && settingsStore.mode === 'mobile'"
168
- class="item"
169
- @click="toggleSearch('menu')"
170
- >
171
- <IconamoonSearch text="14px" />
172
- </span>
173
- <span
174
- v-if="settingsStore.mode === 'pc' && settingsStore.settings.toolbar.enableFullscreen"
175
- class="item"
176
- @click="toggle"
177
- >
178
- <FluentFullScreenMinimize16Filled v-if="isFullscreen" text="16px" />
179
- <FluentFullScreenMaximize16Filled v-else text="16px" />
180
- </span>
181
- <span
182
- v-if="settingsStore.settings.toolbar.enablePageReload"
183
- class="item"
184
- @click="mainPage.reload()"
185
- >
186
- <FluentArrowSync16Filled text="15px" />
187
- </span>
188
- <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>
189
126
  </div>
127
+
190
128
  <div flex-center cursor-pointer gap-1>
191
129
  <img
192
130
  v-if="userStore.user.avatar && !avatarError"
193
131
  :src="userStore.user.avatar"
194
132
  :onerror="() => (avatarError = true)"
195
- class="h-[24px] w-[24px] rounded-full"
133
+ class="size-[24px] rounded-full"
196
134
  >
197
135
  <CarbonUserAvatarFilledAlt v-else text="20px" mr-2px />
198
136
  <div class="flex-center cursor-pointer">
199
137
  {{ userStore.userOrgName }}
200
138
  </div>
201
- <PhLineVerticalThin />
202
- <HDropdownMenu :items="userMenu">
139
+ <PhLineVerticalThin v-if="userStore?.userOrgName" />
140
+ <HDropdownMenu :items="menus">
203
141
  <div flex-center cursor-pointer gap-1>
204
142
  {{ userStore.user.account }}
205
143
  <MaterialSymbolsExpandMoreRounded ml="5px" mr="10px" />
@@ -208,13 +146,7 @@ const searchComponentsClass = computed(() => {
208
146
  </div>
209
147
 
210
148
  <HotkeysIntro ref="hotkeysIntroRef" />
211
- <Search />
149
+ <SearchPanel />
212
150
  <Preferences v-if="settingsStore.settings.app.enableUserPreferences" ref="preferencesRef" />
213
151
  </div>
214
152
  </template>
215
-
216
- <style scoped>
217
- .item {
218
- --at-apply: flex px-2 py-1 cursor-pointer;
219
- }
220
- </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[];