1717 <router-link class =" site-nav-link" to =" /" :class =" { active: isActive('/') }" >首页</router-link >
1818 <router-link class =" site-nav-link" to =" /all" :class =" { active: isActive('/all') }" >博客</router-link >
1919 <router-link class =" site-nav-link" to =" /about" :class =" { active: isActive('/about') }" >关于</router-link >
20- <a
21- class =" site-nav-link"
22- href =" http://ai.wecode.xin/"
23- target =" _blank"
24- rel =" noopener noreferrer"
25- >
26- AI平台
27- </a >
20+ <div ref =" dropdownRoot" class =" site-nav-dropdown" >
21+ <button
22+ type =" button"
23+ class =" site-nav-link site-nav-link-btn site-nav-dropdown-trigger"
24+ :aria-expanded =" dropdownOpen"
25+ aria-haspopup =" menu"
26+ @click.stop =" toggleDropdown"
27+ >
28+ AI实验室 <span class =" site-nav-dropdown-caret" aria-hidden =" true" >▾</span >
29+ </button >
30+
31+ <div v-if =" dropdownOpen" class =" site-nav-dropdown-menu" role =" menu" >
32+ <a
33+ class =" site-nav-dropdown-item"
34+ href =" http://ai.wecode.xin/"
35+ target =" _blank"
36+ rel =" noopener noreferrer"
37+ role =" menuitem"
38+ @click =" closeDropdown"
39+ >
40+ AI工作平台
41+ </a >
42+ </div >
43+ </div >
2844 <a
2945 class =" site-nav-link"
3046 href =" https://github.com/yyyCode/OpenBlog.git"
4258</template >
4359
4460<script setup>
45- import { onMounted , ref } from ' vue'
61+ import { onBeforeUnmount , onMounted , ref } from ' vue'
4662import { useRoute } from ' vue-router'
4763import { toggleTheme as applyToggle } from ' ../theme'
4864
4965const isDark = ref (false )
5066const route = useRoute ()
5167const emit = defineEmits ([' toggle-widgets' ])
5268
69+ const dropdownOpen = ref (false )
70+ const dropdownRoot = ref (null )
71+
5372function sync () {
5473 isDark .value = document .documentElement .getAttribute (' data-theme' ) === ' dark'
5574}
@@ -58,6 +77,31 @@ onMounted(() => {
5877 sync ()
5978})
6079
80+ function toggleDropdown () {
81+ dropdownOpen .value = ! dropdownOpen .value
82+ }
83+
84+ function closeDropdown () {
85+ dropdownOpen .value = false
86+ }
87+
88+ function onDocClick (e ) {
89+ const el = dropdownRoot .value
90+ if (! el) return
91+ // 点击下拉触发器或菜单内部:不收起
92+ const target = e? .target
93+ if (target && el .contains (target)) return
94+ closeDropdown ()
95+ }
96+
97+ onMounted (() => {
98+ document .addEventListener (' click' , onDocClick)
99+ })
100+
101+ onBeforeUnmount (() => {
102+ document .removeEventListener (' click' , onDocClick)
103+ })
104+
61105function onToggle () {
62106 applyToggle ()
63107 sync ()
@@ -67,3 +111,54 @@ function isActive(path) {
67111 return route .path === path
68112}
69113< / script>
114+
115+ < style scoped>
116+ .site - nav- dropdown {
117+ position: relative;
118+ display: inline- flex;
119+ }
120+
121+ .site - nav- dropdown- trigger {
122+ display: inline- flex;
123+ align- items: center;
124+ gap: 6px ;
125+ white- space: nowrap;
126+ }
127+
128+ .site - nav- dropdown- caret {
129+ font- size: 12px ;
130+ opacity: 0.85 ;
131+ transform: translateY (- 1px );
132+ }
133+
134+ .site - nav- dropdown- menu {
135+ position: absolute;
136+ left: 0 ;
137+ top: calc (100 % + 8px );
138+ background: var (-- bg);
139+ border: 1px solid var (-- border);
140+ border- radius: 10px ;
141+ padding: 6px ;
142+ min- width: 160px ;
143+ box- shadow: var (-- shadow);
144+ z- index: 1000 ;
145+ }
146+
147+ .site - nav- dropdown- item {
148+ display: block;
149+ text- decoration: none;
150+ color: var (-- muted);
151+ font- weight: 600 ;
152+ font- size: 13px ;
153+ padding: 10px 12px ;
154+ border- radius: 8px ;
155+ transition:
156+ background 0 .15s ease,
157+ color 0 .15s ease;
158+ }
159+
160+ .site - nav- dropdown- item: hover {
161+ color: var (-- text);
162+ background: var (-- accent- bg);
163+ }
164+ < / style>
0 commit comments