xto-fronted 0.3.3 → 0.3.4
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.
- package/dist/components/Layout/Header.vue.d.ts +1 -0
- package/dist/components/Layout/Sidebar.vue.d.ts +8 -1
- package/dist/index-Bn4ThpX9.js +142 -0
- package/dist/index-CmQfZC8r.js +372 -0
- package/dist/index-Dga14ZN7.js +1774 -0
- package/dist/index-fyarVCog.js +475 -0
- package/dist/index-orZCyV6I.js +345 -0
- package/dist/index.es.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/Layout/Header.vue +466 -21
- package/src/components/Layout/Sidebar.vue +55 -11
- package/src/components/Layout/index.vue +181 -7
|
@@ -6,6 +6,8 @@ import { useUserStore } from '@/stores/user'
|
|
|
6
6
|
import { useAuthStore } from '@/stores/auth'
|
|
7
7
|
import { useMenuStore } from '@/stores/menu'
|
|
8
8
|
|
|
9
|
+
type LayoutMode = 'sidebar' | 'top' | 'mix'
|
|
10
|
+
|
|
9
11
|
const route = useRoute()
|
|
10
12
|
const router = useRouter()
|
|
11
13
|
const appStore = useAppStore()
|
|
@@ -13,10 +15,30 @@ const userStore = useUserStore()
|
|
|
13
15
|
const authStore = useAuthStore()
|
|
14
16
|
const menuStore = useMenuStore()
|
|
15
17
|
|
|
16
|
-
const isCollapsed = computed(() => appStore.isCollapsed)
|
|
17
18
|
const dropdownVisible = ref(false)
|
|
19
|
+
const layoutDropdownVisible = ref(false)
|
|
18
20
|
const dropdownRef = ref<HTMLElement | null>(null)
|
|
19
21
|
const isFullscreen = ref(false)
|
|
22
|
+
const searchVisible = ref(false)
|
|
23
|
+
const searchKeyword = ref('')
|
|
24
|
+
const searchRef = ref<HTMLElement | null>(null)
|
|
25
|
+
|
|
26
|
+
// 布局模式选项
|
|
27
|
+
const layoutOptions: { value: LayoutMode; label: string; icon: string }[] = [
|
|
28
|
+
{ value: 'sidebar', label: '左侧菜单', icon: '📋' },
|
|
29
|
+
{ value: 'top', label: '顶部菜单', icon: '☰' },
|
|
30
|
+
{ value: 'mix', label: '混合菜单', icon: '⚡' }
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
// 主题色选项
|
|
34
|
+
const colorOptions = [
|
|
35
|
+
{ value: '#409eff', label: '默认蓝' },
|
|
36
|
+
{ value: '#1890ff', label: '科技蓝' },
|
|
37
|
+
{ value: '#52c41a', label: '极光绿' },
|
|
38
|
+
{ value: '#faad14', label: '日落橙' },
|
|
39
|
+
{ value: '#f5222d', label: '薄暮红' },
|
|
40
|
+
{ value: '#722ed1', label: '酱紫' }
|
|
41
|
+
]
|
|
20
42
|
|
|
21
43
|
// 面包屑
|
|
22
44
|
const breadcrumbs = computed(() => {
|
|
@@ -27,6 +49,31 @@ const breadcrumbs = computed(() => {
|
|
|
27
49
|
}))
|
|
28
50
|
})
|
|
29
51
|
|
|
52
|
+
// 扁平化菜单用于搜索
|
|
53
|
+
const flattenMenus = (menus: any[], parentTitle = ''): any[] => {
|
|
54
|
+
const result: any[] = []
|
|
55
|
+
menus.forEach(menu => {
|
|
56
|
+
if (menu.children && menu.children.length > 0) {
|
|
57
|
+
result.push(...flattenMenus(menu.children, menu.title))
|
|
58
|
+
} else {
|
|
59
|
+
result.push({ ...menu, parentTitle })
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
return result
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 搜索结果
|
|
66
|
+
const searchResults = computed(() => {
|
|
67
|
+
if (!searchKeyword.value.trim()) return []
|
|
68
|
+
const flatMenus = flattenMenus(menuStore.menuList)
|
|
69
|
+
return flatMenus.filter(menu =>
|
|
70
|
+
menu.title.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
|
71
|
+
)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// 当前布局模式
|
|
75
|
+
const currentLayout = computed(() => appStore.layout)
|
|
76
|
+
|
|
30
77
|
// 切换折叠
|
|
31
78
|
const toggleCollapse = () => {
|
|
32
79
|
appStore.toggleCollapse()
|
|
@@ -37,6 +84,22 @@ const toggleTheme = () => {
|
|
|
37
84
|
appStore.toggleTheme()
|
|
38
85
|
}
|
|
39
86
|
|
|
87
|
+
// 切换布局模式
|
|
88
|
+
const handleLayoutChange = (mode: LayoutMode) => {
|
|
89
|
+
appStore.setLayout(mode)
|
|
90
|
+
layoutDropdownVisible.value = false
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 切换灰色模式
|
|
94
|
+
const toggleGreyMode = () => {
|
|
95
|
+
const html = document.documentElement
|
|
96
|
+
if (html.classList.contains('grey-mode')) {
|
|
97
|
+
html.classList.remove('grey-mode')
|
|
98
|
+
} else {
|
|
99
|
+
html.classList.add('grey-mode')
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
40
103
|
// 切换全屏
|
|
41
104
|
const toggleFullscreen = () => {
|
|
42
105
|
if (!document.fullscreenElement) {
|
|
@@ -54,30 +117,58 @@ const handleFullscreenChange = () => {
|
|
|
54
117
|
// 切换下拉菜单
|
|
55
118
|
const toggleDropdown = () => {
|
|
56
119
|
dropdownVisible.value = !dropdownVisible.value
|
|
120
|
+
layoutDropdownVisible.value = false
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 切换布局下拉菜单
|
|
124
|
+
const toggleLayoutDropdown = () => {
|
|
125
|
+
layoutDropdownVisible.value = !layoutDropdownVisible.value
|
|
126
|
+
dropdownVisible.value = false
|
|
57
127
|
}
|
|
58
128
|
|
|
59
129
|
// 关闭下拉菜单
|
|
60
|
-
const
|
|
130
|
+
const closeDropdowns = () => {
|
|
61
131
|
dropdownVisible.value = false
|
|
132
|
+
layoutDropdownVisible.value = false
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 显示搜索
|
|
136
|
+
const showSearch = () => {
|
|
137
|
+
searchVisible.value = true
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 隐藏搜索
|
|
141
|
+
const hideSearch = () => {
|
|
142
|
+
searchVisible.value = false
|
|
143
|
+
searchKeyword.value = ''
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 搜索结果点击
|
|
147
|
+
const handleSearchItemClick = (path: string) => {
|
|
148
|
+
router.push(path)
|
|
149
|
+
hideSearch()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 设置主题色
|
|
153
|
+
const handleColorChange = (color: string) => {
|
|
154
|
+
appStore.setPrimaryColor(color)
|
|
62
155
|
}
|
|
63
156
|
|
|
64
157
|
// 个人信息
|
|
65
158
|
const handleProfile = () => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
alert('个人信息功能开发中...')
|
|
159
|
+
closeDropdowns()
|
|
160
|
+
router.push('/profile')
|
|
69
161
|
}
|
|
70
162
|
|
|
71
163
|
// 修改密码
|
|
72
164
|
const handleChangePassword = () => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
alert('修改密码功能开发中...')
|
|
165
|
+
closeDropdowns()
|
|
166
|
+
router.push('/change-password')
|
|
76
167
|
}
|
|
77
168
|
|
|
78
169
|
// 退出登录
|
|
79
170
|
const handleLogout = () => {
|
|
80
|
-
|
|
171
|
+
closeDropdowns()
|
|
81
172
|
authStore.logout()
|
|
82
173
|
userStore.clearUserInfo()
|
|
83
174
|
menuStore.clearMenu()
|
|
@@ -87,18 +178,37 @@ const handleLogout = () => {
|
|
|
87
178
|
// 点击外部关闭下拉菜单
|
|
88
179
|
const handleClickOutside = (event: MouseEvent) => {
|
|
89
180
|
if (dropdownRef.value && !dropdownRef.value.contains(event.target as Node)) {
|
|
90
|
-
|
|
181
|
+
closeDropdowns()
|
|
182
|
+
}
|
|
183
|
+
if (searchRef.value && !searchRef.value.contains(event.target as Node)) {
|
|
184
|
+
hideSearch()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 键盘快捷键
|
|
189
|
+
const handleKeydown = (event: KeyboardEvent) => {
|
|
190
|
+
if (event.key === 'Escape') {
|
|
191
|
+
hideSearch()
|
|
192
|
+
closeDropdowns()
|
|
193
|
+
}
|
|
194
|
+
// Ctrl+K 打开搜索
|
|
195
|
+
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
|
|
196
|
+
event.preventDefault()
|
|
197
|
+
showSearch()
|
|
91
198
|
}
|
|
92
199
|
}
|
|
93
200
|
|
|
94
201
|
onMounted(() => {
|
|
95
202
|
document.addEventListener('click', handleClickOutside)
|
|
96
203
|
document.addEventListener('fullscreenchange', handleFullscreenChange)
|
|
204
|
+
document.addEventListener('keydown', handleKeydown)
|
|
205
|
+
appStore.initTheme()
|
|
97
206
|
})
|
|
98
207
|
|
|
99
208
|
onUnmounted(() => {
|
|
100
209
|
document.removeEventListener('click', handleClickOutside)
|
|
101
210
|
document.removeEventListener('fullscreenchange', handleFullscreenChange)
|
|
211
|
+
document.removeEventListener('keydown', handleKeydown)
|
|
102
212
|
})
|
|
103
213
|
</script>
|
|
104
214
|
|
|
@@ -106,13 +216,18 @@ onUnmounted(() => {
|
|
|
106
216
|
<div class="header">
|
|
107
217
|
<!-- 左侧 -->
|
|
108
218
|
<div class="header__left">
|
|
219
|
+
<!-- 折叠按钮 -->
|
|
109
220
|
<div class="header__collapse" @click="toggleCollapse">
|
|
110
|
-
<
|
|
111
|
-
|
|
221
|
+
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
|
|
222
|
+
<path v-if="appStore.isCollapsed" d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>
|
|
223
|
+
<path v-else d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>
|
|
224
|
+
</svg>
|
|
112
225
|
</div>
|
|
113
|
-
|
|
226
|
+
|
|
227
|
+
<!-- 面包屑 -->
|
|
228
|
+
<div v-if="appStore.showBreadcrumb" class="header__breadcrumb">
|
|
114
229
|
<span v-for="(item, index) in breadcrumbs" :key="item.path">
|
|
115
|
-
<span v-if="index > 0"
|
|
230
|
+
<span v-if="index > 0" class="breadcrumb-separator">/</span>
|
|
116
231
|
<span :class="{ 'is-current': index === breadcrumbs.length - 1 }">
|
|
117
232
|
{{ item.title }}
|
|
118
233
|
</span>
|
|
@@ -122,8 +237,15 @@ onUnmounted(() => {
|
|
|
122
237
|
|
|
123
238
|
<!-- 右侧 -->
|
|
124
239
|
<div class="header__right">
|
|
240
|
+
<!-- 搜索按钮 -->
|
|
241
|
+
<div class="header__action" @click="showSearch" title="搜索 (Ctrl+K)">
|
|
242
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
|
|
243
|
+
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
|
|
244
|
+
</svg>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
125
247
|
<!-- 全屏切换 -->
|
|
126
|
-
<div class="header__action" @click="toggleFullscreen" :title="isFullscreen ? '
|
|
248
|
+
<div class="header__action" @click="toggleFullscreen" :title="isFullscreen ? '退出全屏' : '全屏'">
|
|
127
249
|
<svg v-if="isFullscreen" viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
|
|
128
250
|
<path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>
|
|
129
251
|
</svg>
|
|
@@ -132,10 +254,58 @@ onUnmounted(() => {
|
|
|
132
254
|
</svg>
|
|
133
255
|
</div>
|
|
134
256
|
|
|
257
|
+
<!-- 布局切换 -->
|
|
258
|
+
<div class="header__action header__layout" ref="dropdownRef">
|
|
259
|
+
<div class="header__action-trigger" @click.stop="toggleLayoutDropdown">
|
|
260
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
|
|
261
|
+
<path d="M3 3h18v18H3V3zm2 2v6h14V5H5zm0 8v6h6v-6H5zm8 0v6h6v-6h-6z"/>
|
|
262
|
+
</svg>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<!-- 布局下拉菜单 -->
|
|
266
|
+
<Transition name="dropdown">
|
|
267
|
+
<div v-if="layoutDropdownVisible" class="header__layout-dropdown">
|
|
268
|
+
<div class="header__layout-title">布局模式</div>
|
|
269
|
+
<div class="header__layout-options">
|
|
270
|
+
<div
|
|
271
|
+
v-for="option in layoutOptions"
|
|
272
|
+
:key="option.value"
|
|
273
|
+
:class="['header__layout-option', { 'is-active': currentLayout === option.value }]"
|
|
274
|
+
@click="handleLayoutChange(option.value)"
|
|
275
|
+
>
|
|
276
|
+
<span class="layout-icon">{{ option.icon }}</span>
|
|
277
|
+
<span>{{ option.label }}</span>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
<div class="header__layout-divider"></div>
|
|
281
|
+
<div class="header__layout-title">主题色</div>
|
|
282
|
+
<div class="header__color-options">
|
|
283
|
+
<div
|
|
284
|
+
v-for="color in colorOptions"
|
|
285
|
+
:key="color.value"
|
|
286
|
+
:class="['header__color-option', { 'is-active': appStore.primaryColor === color.value }]"
|
|
287
|
+
:style="{ backgroundColor: color.value }"
|
|
288
|
+
:title="color.label"
|
|
289
|
+
@click="handleColorChange(color.value)"
|
|
290
|
+
></div>
|
|
291
|
+
</div>
|
|
292
|
+
<div class="header__layout-divider"></div>
|
|
293
|
+
<div class="header__layout-item" @click="toggleGreyMode">
|
|
294
|
+
<span class="layout-icon">🌫️</span>
|
|
295
|
+
<span>灰色模式</span>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
</Transition>
|
|
299
|
+
</div>
|
|
300
|
+
|
|
135
301
|
<!-- 主题切换 -->
|
|
136
302
|
<div class="header__action" @click="toggleTheme" title="切换主题">
|
|
137
|
-
<
|
|
138
|
-
|
|
303
|
+
<svg v-if="appStore.isDark" viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
|
|
304
|
+
<path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-3.03 0-5.5-2.47-5.5-5.5 0-1.82.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z"/>
|
|
305
|
+
</svg>
|
|
306
|
+
<svg v-else viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
|
|
307
|
+
<path d="M6.76 4.84l-1.4-1.4-1.41 1.41 1.4 1.4 1.41-1.41zm10.49 10.49l-1.4-1.4-1.41 1.41 1.4 1.4 1.41-1.41zM12 4c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7zm1-9h-2v4h2v-4zm0-6h-2v2h2V5zM4.84 17.24l-1.41 1.41 1.4 1.4 1.41-1.41-1.4-1.4z"/>
|
|
308
|
+
</svg>
|
|
139
309
|
</div>
|
|
140
310
|
|
|
141
311
|
<!-- 用户头像 -->
|
|
@@ -157,7 +327,7 @@ onUnmounted(() => {
|
|
|
157
327
|
</div>
|
|
158
328
|
<div class="header__dropdown-info">
|
|
159
329
|
<div class="header__dropdown-name">{{ userStore.nickname }}</div>
|
|
160
|
-
<div class="header__dropdown-role">{{ userStore.roles
|
|
330
|
+
<div class="header__dropdown-role">{{ userStore.roles?.join(', ') }}</div>
|
|
161
331
|
</div>
|
|
162
332
|
</div>
|
|
163
333
|
<div class="header__dropdown-divider"></div>
|
|
@@ -180,6 +350,45 @@ onUnmounted(() => {
|
|
|
180
350
|
</Transition>
|
|
181
351
|
</div>
|
|
182
352
|
</div>
|
|
353
|
+
|
|
354
|
+
<!-- 全局搜索弹窗 -->
|
|
355
|
+
<Transition name="search">
|
|
356
|
+
<div v-if="searchVisible" class="header__search-modal" ref="searchRef">
|
|
357
|
+
<div class="search-container">
|
|
358
|
+
<div class="search-input-wrapper">
|
|
359
|
+
<svg class="search-icon" viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
|
|
360
|
+
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
|
|
361
|
+
</svg>
|
|
362
|
+
<input
|
|
363
|
+
v-model="searchKeyword"
|
|
364
|
+
type="text"
|
|
365
|
+
class="search-input"
|
|
366
|
+
placeholder="搜索菜单..."
|
|
367
|
+
autofocus
|
|
368
|
+
@keyup.enter="searchResults[0] && handleSearchItemClick(searchResults[0].path)"
|
|
369
|
+
/>
|
|
370
|
+
<span class="search-shortcut">ESC 关闭</span>
|
|
371
|
+
</div>
|
|
372
|
+
<div v-if="searchResults.length > 0" class="search-results">
|
|
373
|
+
<div
|
|
374
|
+
v-for="(item, index) in searchResults"
|
|
375
|
+
:key="item.path"
|
|
376
|
+
:class="['search-result-item', { 'is-first': index === 0 }]"
|
|
377
|
+
@click="handleSearchItemClick(item.path)"
|
|
378
|
+
>
|
|
379
|
+
<span class="search-result-icon">{{ item.icon || '📄' }}</span>
|
|
380
|
+
<div class="search-result-info">
|
|
381
|
+
<span class="search-result-title">{{ item.title }}</span>
|
|
382
|
+
<span v-if="item.parentTitle" class="search-result-parent">{{ item.parentTitle }}</span>
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
<div v-else-if="searchKeyword" class="search-empty">
|
|
387
|
+
未找到匹配的菜单
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
</Transition>
|
|
183
392
|
</div>
|
|
184
393
|
</template>
|
|
185
394
|
|
|
@@ -190,6 +399,8 @@ onUnmounted(() => {
|
|
|
190
399
|
justify-content: space-between;
|
|
191
400
|
padding: 0 20px;
|
|
192
401
|
height: 100%;
|
|
402
|
+
background-color: var(--bg-color);
|
|
403
|
+
border-bottom: 1px solid var(--color-border-lighter);
|
|
193
404
|
|
|
194
405
|
&__left {
|
|
195
406
|
display: flex;
|
|
@@ -204,8 +415,8 @@ onUnmounted(() => {
|
|
|
204
415
|
align-items: center;
|
|
205
416
|
justify-content: center;
|
|
206
417
|
cursor: pointer;
|
|
207
|
-
font-size: 18px;
|
|
208
418
|
color: var(--color-text-regular);
|
|
419
|
+
transition: color 0.2s;
|
|
209
420
|
|
|
210
421
|
&:hover {
|
|
211
422
|
color: var(--color-primary);
|
|
@@ -216,6 +427,11 @@ onUnmounted(() => {
|
|
|
216
427
|
font-size: 14px;
|
|
217
428
|
color: var(--color-text-secondary);
|
|
218
429
|
|
|
430
|
+
.breadcrumb-separator {
|
|
431
|
+
margin: 0 8px;
|
|
432
|
+
color: var(--color-text-placeholder);
|
|
433
|
+
}
|
|
434
|
+
|
|
219
435
|
.is-current {
|
|
220
436
|
color: var(--color-text-primary);
|
|
221
437
|
font-weight: 500;
|
|
@@ -225,7 +441,7 @@ onUnmounted(() => {
|
|
|
225
441
|
&__right {
|
|
226
442
|
display: flex;
|
|
227
443
|
align-items: center;
|
|
228
|
-
gap:
|
|
444
|
+
gap: 8px;
|
|
229
445
|
}
|
|
230
446
|
|
|
231
447
|
&__action {
|
|
@@ -236,7 +452,107 @@ onUnmounted(() => {
|
|
|
236
452
|
justify-content: center;
|
|
237
453
|
cursor: pointer;
|
|
238
454
|
border-radius: var(--border-radius-base);
|
|
239
|
-
|
|
455
|
+
color: var(--color-text-regular);
|
|
456
|
+
transition: all 0.2s;
|
|
457
|
+
|
|
458
|
+
&:hover {
|
|
459
|
+
background-color: var(--color-fill);
|
|
460
|
+
color: var(--color-primary);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
&__layout {
|
|
465
|
+
position: relative;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
&__layout-dropdown {
|
|
469
|
+
position: absolute;
|
|
470
|
+
top: calc(100% + 8px);
|
|
471
|
+
right: 0;
|
|
472
|
+
min-width: 200px;
|
|
473
|
+
background-color: var(--bg-color);
|
|
474
|
+
border-radius: var(--border-radius-base);
|
|
475
|
+
box-shadow: var(--box-shadow);
|
|
476
|
+
padding: 12px;
|
|
477
|
+
z-index: 100;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
&__layout-title {
|
|
481
|
+
font-size: 12px;
|
|
482
|
+
color: var(--color-text-secondary);
|
|
483
|
+
margin-bottom: 8px;
|
|
484
|
+
padding-left: 4px;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
&__layout-options {
|
|
488
|
+
display: flex;
|
|
489
|
+
flex-direction: column;
|
|
490
|
+
gap: 4px;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
&__layout-option {
|
|
494
|
+
display: flex;
|
|
495
|
+
align-items: center;
|
|
496
|
+
gap: 8px;
|
|
497
|
+
padding: 8px 12px;
|
|
498
|
+
cursor: pointer;
|
|
499
|
+
border-radius: var(--border-radius-base);
|
|
500
|
+
font-size: 14px;
|
|
501
|
+
color: var(--color-text-regular);
|
|
502
|
+
transition: all 0.2s;
|
|
503
|
+
|
|
504
|
+
&:hover {
|
|
505
|
+
background-color: var(--color-fill);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
&.is-active {
|
|
509
|
+
color: var(--color-primary);
|
|
510
|
+
background-color: var(--color-primary-light-9);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.layout-icon {
|
|
514
|
+
font-size: 16px;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
&__color-options {
|
|
519
|
+
display: flex;
|
|
520
|
+
gap: 8px;
|
|
521
|
+
padding: 8px 0;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
&__color-option {
|
|
525
|
+
width: 20px;
|
|
526
|
+
height: 20px;
|
|
527
|
+
border-radius: 50%;
|
|
528
|
+
cursor: pointer;
|
|
529
|
+
transition: transform 0.2s;
|
|
530
|
+
|
|
531
|
+
&:hover {
|
|
532
|
+
transform: scale(1.2);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
&.is-active {
|
|
536
|
+
box-shadow: 0 0 0 2px var(--bg-color), 0 0 0 4px currentColor;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
&__layout-divider {
|
|
541
|
+
height: 1px;
|
|
542
|
+
background-color: var(--color-border-lighter);
|
|
543
|
+
margin: 8px 0;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
&__layout-item {
|
|
547
|
+
display: flex;
|
|
548
|
+
align-items: center;
|
|
549
|
+
gap: 8px;
|
|
550
|
+
padding: 8px 12px;
|
|
551
|
+
cursor: pointer;
|
|
552
|
+
border-radius: var(--border-radius-base);
|
|
553
|
+
font-size: 14px;
|
|
554
|
+
color: var(--color-text-regular);
|
|
555
|
+
transition: all 0.2s;
|
|
240
556
|
|
|
241
557
|
&:hover {
|
|
242
558
|
background-color: var(--color-fill);
|
|
@@ -245,6 +561,7 @@ onUnmounted(() => {
|
|
|
245
561
|
|
|
246
562
|
&__user {
|
|
247
563
|
position: relative;
|
|
564
|
+
margin-left: 8px;
|
|
248
565
|
|
|
249
566
|
&-trigger {
|
|
250
567
|
display: flex;
|
|
@@ -263,6 +580,10 @@ onUnmounted(() => {
|
|
|
263
580
|
&-name {
|
|
264
581
|
font-size: 14px;
|
|
265
582
|
color: var(--color-text-primary);
|
|
583
|
+
max-width: 100px;
|
|
584
|
+
overflow: hidden;
|
|
585
|
+
text-overflow: ellipsis;
|
|
586
|
+
white-space: nowrap;
|
|
266
587
|
}
|
|
267
588
|
|
|
268
589
|
&-arrow {
|
|
@@ -374,6 +695,114 @@ onUnmounted(() => {
|
|
|
374
695
|
font-size: 16px;
|
|
375
696
|
}
|
|
376
697
|
}
|
|
698
|
+
|
|
699
|
+
&__search-modal {
|
|
700
|
+
position: fixed;
|
|
701
|
+
top: 0;
|
|
702
|
+
left: 0;
|
|
703
|
+
right: 0;
|
|
704
|
+
bottom: 0;
|
|
705
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
706
|
+
display: flex;
|
|
707
|
+
align-items: flex-start;
|
|
708
|
+
justify-content: center;
|
|
709
|
+
padding-top: 100px;
|
|
710
|
+
z-index: 200;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// 搜索容器
|
|
715
|
+
.search-container {
|
|
716
|
+
width: 600px;
|
|
717
|
+
max-width: 90vw;
|
|
718
|
+
background-color: var(--bg-color);
|
|
719
|
+
border-radius: var(--border-radius-large);
|
|
720
|
+
box-shadow: var(--box-shadow-dark);
|
|
721
|
+
overflow: hidden;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
.search-input-wrapper {
|
|
725
|
+
display: flex;
|
|
726
|
+
align-items: center;
|
|
727
|
+
padding: 16px 20px;
|
|
728
|
+
border-bottom: 1px solid var(--color-border-lighter);
|
|
729
|
+
|
|
730
|
+
.search-icon {
|
|
731
|
+
color: var(--color-text-secondary);
|
|
732
|
+
margin-right: 12px;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
.search-input {
|
|
736
|
+
flex: 1;
|
|
737
|
+
font-size: 16px;
|
|
738
|
+
color: var(--color-text-primary);
|
|
739
|
+
background: transparent;
|
|
740
|
+
border: none;
|
|
741
|
+
outline: none;
|
|
742
|
+
|
|
743
|
+
&::placeholder {
|
|
744
|
+
color: var(--color-text-placeholder);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
.search-shortcut {
|
|
749
|
+
font-size: 12px;
|
|
750
|
+
color: var(--color-text-secondary);
|
|
751
|
+
padding: 4px 8px;
|
|
752
|
+
background-color: var(--color-fill);
|
|
753
|
+
border-radius: var(--border-radius-base);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.search-results {
|
|
758
|
+
max-height: 400px;
|
|
759
|
+
overflow-y: auto;
|
|
760
|
+
padding: 8px 0;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
.search-result-item {
|
|
764
|
+
display: flex;
|
|
765
|
+
align-items: center;
|
|
766
|
+
gap: 12px;
|
|
767
|
+
padding: 12px 20px;
|
|
768
|
+
cursor: pointer;
|
|
769
|
+
transition: background-color 0.2s;
|
|
770
|
+
|
|
771
|
+
&:hover {
|
|
772
|
+
background-color: var(--color-fill);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
&.is-first {
|
|
776
|
+
background-color: var(--color-primary-light-9);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
.search-result-icon {
|
|
780
|
+
font-size: 20px;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.search-result-info {
|
|
784
|
+
flex: 1;
|
|
785
|
+
display: flex;
|
|
786
|
+
flex-direction: column;
|
|
787
|
+
gap: 2px;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
.search-result-title {
|
|
791
|
+
font-size: 14px;
|
|
792
|
+
color: var(--color-text-primary);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
.search-result-parent {
|
|
796
|
+
font-size: 12px;
|
|
797
|
+
color: var(--color-text-secondary);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.search-empty {
|
|
802
|
+
padding: 40px 20px;
|
|
803
|
+
text-align: center;
|
|
804
|
+
color: var(--color-text-secondary);
|
|
805
|
+
font-size: 14px;
|
|
377
806
|
}
|
|
378
807
|
|
|
379
808
|
// 下拉动画
|
|
@@ -387,4 +816,20 @@ onUnmounted(() => {
|
|
|
387
816
|
opacity: 0;
|
|
388
817
|
transform: translateY(-10px);
|
|
389
818
|
}
|
|
819
|
+
|
|
820
|
+
// 搜索弹窗动画
|
|
821
|
+
.search-enter-active,
|
|
822
|
+
.search-leave-active {
|
|
823
|
+
transition: all 0.2s ease;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
.search-enter-from,
|
|
827
|
+
.search-leave-to {
|
|
828
|
+
opacity: 0;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// 灰色模式
|
|
832
|
+
:root.grey-mode {
|
|
833
|
+
filter: grayscale(100%);
|
|
834
|
+
}
|
|
390
835
|
</style>
|