xto-fronted 0.3.3 → 0.3.5
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/index-B6DTsC6l.js +1715 -0
- package/dist/index-BGmUwemj.js +372 -0
- package/dist/index-Bn4ThpX9.js +142 -0
- package/dist/index-Cb-SxHJp.js +345 -0
- package/dist/index-CmQfZC8r.js +372 -0
- package/dist/index-Dga14ZN7.js +1774 -0
- package/dist/index-DnJ481u1.js +475 -0
- package/dist/index-YDlNLFVk.js +142 -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 +616 -32
- package/src/components/Layout/Sidebar.vue +28 -22
- package/src/components/Layout/index.vue +3 -1
|
@@ -5,6 +5,10 @@ import { useAppStore } from '@/stores/app'
|
|
|
5
5
|
import { useUserStore } from '@/stores/user'
|
|
6
6
|
import { useAuthStore } from '@/stores/auth'
|
|
7
7
|
import { useMenuStore } from '@/stores/menu'
|
|
8
|
+
import { Icon } from '@xto/base'
|
|
9
|
+
import { Drawer } from '@xto/feedback'
|
|
10
|
+
|
|
11
|
+
type LayoutMode = 'sidebar' | 'top' | 'mix'
|
|
8
12
|
|
|
9
13
|
const route = useRoute()
|
|
10
14
|
const router = useRouter()
|
|
@@ -13,10 +17,33 @@ const userStore = useUserStore()
|
|
|
13
17
|
const authStore = useAuthStore()
|
|
14
18
|
const menuStore = useMenuStore()
|
|
15
19
|
|
|
16
|
-
const isCollapsed = computed(() => appStore.isCollapsed)
|
|
17
20
|
const dropdownVisible = ref(false)
|
|
21
|
+
const drawerVisible = ref(false)
|
|
18
22
|
const dropdownRef = ref<HTMLElement | null>(null)
|
|
19
23
|
const isFullscreen = ref(false)
|
|
24
|
+
const searchVisible = ref(false)
|
|
25
|
+
const searchKeyword = ref('')
|
|
26
|
+
const searchRef = ref<HTMLElement | null>(null)
|
|
27
|
+
|
|
28
|
+
// 灰色模式状态
|
|
29
|
+
const greyMode = ref(false)
|
|
30
|
+
|
|
31
|
+
// 布局模式选项
|
|
32
|
+
const layoutOptions: { value: LayoutMode; label: string; icon: string }[] = [
|
|
33
|
+
{ value: 'sidebar', label: '左侧菜单', icon: 'sidebar-left' },
|
|
34
|
+
{ value: 'top', label: '顶部菜单', icon: 'menu' },
|
|
35
|
+
{ value: 'mix', label: '混合菜单', icon: 'grid' }
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
// 主题色选项
|
|
39
|
+
const colorOptions = [
|
|
40
|
+
{ value: '#409eff', label: '默认蓝' },
|
|
41
|
+
{ value: '#1890ff', label: '科技蓝' },
|
|
42
|
+
{ value: '#52c41a', label: '极光绿' },
|
|
43
|
+
{ value: '#faad14', label: '日落橙' },
|
|
44
|
+
{ value: '#f5222d', label: '薄暮红' },
|
|
45
|
+
{ value: '#722ed1', label: '酱紫' }
|
|
46
|
+
]
|
|
20
47
|
|
|
21
48
|
// 面包屑
|
|
22
49
|
const breadcrumbs = computed(() => {
|
|
@@ -27,6 +54,31 @@ const breadcrumbs = computed(() => {
|
|
|
27
54
|
}))
|
|
28
55
|
})
|
|
29
56
|
|
|
57
|
+
// 扁平化菜单用于搜索
|
|
58
|
+
const flattenMenus = (menus: any[], parentTitle = ''): any[] => {
|
|
59
|
+
const result: any[] = []
|
|
60
|
+
menus.forEach(menu => {
|
|
61
|
+
if (menu.children && menu.children.length > 0) {
|
|
62
|
+
result.push(...flattenMenus(menu.children, menu.title))
|
|
63
|
+
} else {
|
|
64
|
+
result.push({ ...menu, parentTitle })
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
return result
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 搜索结果
|
|
71
|
+
const searchResults = computed(() => {
|
|
72
|
+
if (!searchKeyword.value.trim()) return []
|
|
73
|
+
const flatMenus = flattenMenus(menuStore.menuList)
|
|
74
|
+
return flatMenus.filter(menu =>
|
|
75
|
+
menu.title.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
|
76
|
+
)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// 当前布局模式
|
|
80
|
+
const currentLayout = computed(() => appStore.layout)
|
|
81
|
+
|
|
30
82
|
// 切换折叠
|
|
31
83
|
const toggleCollapse = () => {
|
|
32
84
|
appStore.toggleCollapse()
|
|
@@ -37,6 +89,27 @@ const toggleTheme = () => {
|
|
|
37
89
|
appStore.toggleTheme()
|
|
38
90
|
}
|
|
39
91
|
|
|
92
|
+
// 打开设置抽屉
|
|
93
|
+
const openSettingsDrawer = () => {
|
|
94
|
+
drawerVisible.value = true
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 切换布局模式
|
|
98
|
+
const handleLayoutChange = (mode: LayoutMode) => {
|
|
99
|
+
appStore.setLayout(mode)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 切换灰色模式
|
|
103
|
+
const handleGreyModeChange = (value: boolean) => {
|
|
104
|
+
greyMode.value = value
|
|
105
|
+
const html = document.documentElement
|
|
106
|
+
if (value) {
|
|
107
|
+
html.classList.add('grey-mode')
|
|
108
|
+
} else {
|
|
109
|
+
html.classList.remove('grey-mode')
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
40
113
|
// 切换全屏
|
|
41
114
|
const toggleFullscreen = () => {
|
|
42
115
|
if (!document.fullscreenElement) {
|
|
@@ -57,27 +130,47 @@ const toggleDropdown = () => {
|
|
|
57
130
|
}
|
|
58
131
|
|
|
59
132
|
// 关闭下拉菜单
|
|
60
|
-
const
|
|
133
|
+
const closeDropdowns = () => {
|
|
61
134
|
dropdownVisible.value = false
|
|
62
135
|
}
|
|
63
136
|
|
|
137
|
+
// 显示搜索
|
|
138
|
+
const showSearch = () => {
|
|
139
|
+
searchVisible.value = true
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 隐藏搜索
|
|
143
|
+
const hideSearch = () => {
|
|
144
|
+
searchVisible.value = false
|
|
145
|
+
searchKeyword.value = ''
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 搜索结果点击
|
|
149
|
+
const handleSearchItemClick = (path: string) => {
|
|
150
|
+
router.push(path)
|
|
151
|
+
hideSearch()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 设置主题色
|
|
155
|
+
const handleColorChange = (color: string) => {
|
|
156
|
+
appStore.setPrimaryColor(color)
|
|
157
|
+
}
|
|
158
|
+
|
|
64
159
|
// 个人信息
|
|
65
160
|
const handleProfile = () => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
alert('个人信息功能开发中...')
|
|
161
|
+
closeDropdowns()
|
|
162
|
+
router.push('/profile')
|
|
69
163
|
}
|
|
70
164
|
|
|
71
165
|
// 修改密码
|
|
72
166
|
const handleChangePassword = () => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
alert('修改密码功能开发中...')
|
|
167
|
+
closeDropdowns()
|
|
168
|
+
router.push('/change-password')
|
|
76
169
|
}
|
|
77
170
|
|
|
78
171
|
// 退出登录
|
|
79
172
|
const handleLogout = () => {
|
|
80
|
-
|
|
173
|
+
closeDropdowns()
|
|
81
174
|
authStore.logout()
|
|
82
175
|
userStore.clearUserInfo()
|
|
83
176
|
menuStore.clearMenu()
|
|
@@ -87,18 +180,39 @@ const handleLogout = () => {
|
|
|
87
180
|
// 点击外部关闭下拉菜单
|
|
88
181
|
const handleClickOutside = (event: MouseEvent) => {
|
|
89
182
|
if (dropdownRef.value && !dropdownRef.value.contains(event.target as Node)) {
|
|
90
|
-
|
|
183
|
+
closeDropdowns()
|
|
184
|
+
}
|
|
185
|
+
if (searchRef.value && !searchRef.value.contains(event.target as Node)) {
|
|
186
|
+
hideSearch()
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 键盘快捷键
|
|
191
|
+
const handleKeydown = (event: KeyboardEvent) => {
|
|
192
|
+
if (event.key === 'Escape') {
|
|
193
|
+
hideSearch()
|
|
194
|
+
closeDropdowns()
|
|
195
|
+
}
|
|
196
|
+
// Ctrl+K 打开搜索
|
|
197
|
+
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
|
|
198
|
+
event.preventDefault()
|
|
199
|
+
showSearch()
|
|
91
200
|
}
|
|
92
201
|
}
|
|
93
202
|
|
|
94
203
|
onMounted(() => {
|
|
95
204
|
document.addEventListener('click', handleClickOutside)
|
|
96
205
|
document.addEventListener('fullscreenchange', handleFullscreenChange)
|
|
206
|
+
document.addEventListener('keydown', handleKeydown)
|
|
207
|
+
appStore.initTheme()
|
|
208
|
+
// 初始化灰色模式状态
|
|
209
|
+
greyMode.value = document.documentElement.classList.contains('grey-mode')
|
|
97
210
|
})
|
|
98
211
|
|
|
99
212
|
onUnmounted(() => {
|
|
100
213
|
document.removeEventListener('click', handleClickOutside)
|
|
101
214
|
document.removeEventListener('fullscreenchange', handleFullscreenChange)
|
|
215
|
+
document.removeEventListener('keydown', handleKeydown)
|
|
102
216
|
})
|
|
103
217
|
</script>
|
|
104
218
|
|
|
@@ -106,13 +220,15 @@ onUnmounted(() => {
|
|
|
106
220
|
<div class="header">
|
|
107
221
|
<!-- 左侧 -->
|
|
108
222
|
<div class="header__left">
|
|
223
|
+
<!-- 折叠按钮 -->
|
|
109
224
|
<div class="header__collapse" @click="toggleCollapse">
|
|
110
|
-
<
|
|
111
|
-
<span v-else>☰</span>
|
|
225
|
+
<Icon :name="appStore.isCollapsed ? 'menu-unfold' : 'menu-fold'" :size="18" />
|
|
112
226
|
</div>
|
|
113
|
-
|
|
227
|
+
|
|
228
|
+
<!-- 面包屑 -->
|
|
229
|
+
<div v-if="appStore.showBreadcrumb" class="header__breadcrumb">
|
|
114
230
|
<span v-for="(item, index) in breadcrumbs" :key="item.path">
|
|
115
|
-
<span v-if="index > 0"
|
|
231
|
+
<span v-if="index > 0" class="breadcrumb-separator">/</span>
|
|
116
232
|
<span :class="{ 'is-current': index === breadcrumbs.length - 1 }">
|
|
117
233
|
{{ item.title }}
|
|
118
234
|
</span>
|
|
@@ -122,20 +238,24 @@ onUnmounted(() => {
|
|
|
122
238
|
|
|
123
239
|
<!-- 右侧 -->
|
|
124
240
|
<div class="header__right">
|
|
241
|
+
<!-- 搜索按钮 -->
|
|
242
|
+
<div class="header__action" @click="showSearch" title="搜索 (Ctrl+K)">
|
|
243
|
+
<Icon name="search" :size="16" />
|
|
244
|
+
</div>
|
|
245
|
+
|
|
125
246
|
<!-- 全屏切换 -->
|
|
126
|
-
<div class="header__action" @click="toggleFullscreen" :title="isFullscreen ? '
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
247
|
+
<div class="header__action" @click="toggleFullscreen" :title="isFullscreen ? '退出全屏' : '全屏'">
|
|
248
|
+
<Icon :name="isFullscreen ? 'fullscreen-exit' : 'fullscreen'" :size="16" />
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<!-- 换肤设置 -->
|
|
252
|
+
<div class="header__action" @click="openSettingsDrawer" title="换肤设置">
|
|
253
|
+
<Icon name="setting" :size="16" />
|
|
133
254
|
</div>
|
|
134
255
|
|
|
135
256
|
<!-- 主题切换 -->
|
|
136
257
|
<div class="header__action" @click="toggleTheme" title="切换主题">
|
|
137
|
-
<
|
|
138
|
-
<span v-else>☀️</span>
|
|
258
|
+
<Icon :name="appStore.isDark ? 'sun' : 'moon'" :size="16" />
|
|
139
259
|
</div>
|
|
140
260
|
|
|
141
261
|
<!-- 用户头像 -->
|
|
@@ -157,22 +277,22 @@ onUnmounted(() => {
|
|
|
157
277
|
</div>
|
|
158
278
|
<div class="header__dropdown-info">
|
|
159
279
|
<div class="header__dropdown-name">{{ userStore.nickname }}</div>
|
|
160
|
-
<div class="header__dropdown-role">{{ userStore.roles
|
|
280
|
+
<div class="header__dropdown-role">{{ userStore.roles?.join(', ') }}</div>
|
|
161
281
|
</div>
|
|
162
282
|
</div>
|
|
163
283
|
<div class="header__dropdown-divider"></div>
|
|
164
284
|
<div class="header__dropdown-menu">
|
|
165
285
|
<div class="header__dropdown-item" @click="handleProfile">
|
|
166
|
-
<
|
|
286
|
+
<Icon name="user" :size="16" />
|
|
167
287
|
<span>个人信息</span>
|
|
168
288
|
</div>
|
|
169
289
|
<div class="header__dropdown-item" @click="handleChangePassword">
|
|
170
|
-
<
|
|
290
|
+
<Icon name="lock" :size="16" />
|
|
171
291
|
<span>修改密码</span>
|
|
172
292
|
</div>
|
|
173
293
|
<div class="header__dropdown-divider"></div>
|
|
174
294
|
<div class="header__dropdown-item header__dropdown-item--danger" @click="handleLogout">
|
|
175
|
-
<
|
|
295
|
+
<Icon name="logout" :size="16" />
|
|
176
296
|
<span>退出登录</span>
|
|
177
297
|
</div>
|
|
178
298
|
</div>
|
|
@@ -180,6 +300,126 @@ onUnmounted(() => {
|
|
|
180
300
|
</Transition>
|
|
181
301
|
</div>
|
|
182
302
|
</div>
|
|
303
|
+
|
|
304
|
+
<!-- 全局搜索弹窗 -->
|
|
305
|
+
<Transition name="search">
|
|
306
|
+
<div v-if="searchVisible" class="header__search-modal" ref="searchRef">
|
|
307
|
+
<div class="search-container">
|
|
308
|
+
<div class="search-input-wrapper">
|
|
309
|
+
<Icon class="search-icon" name="search" :size="20" />
|
|
310
|
+
<input
|
|
311
|
+
v-model="searchKeyword"
|
|
312
|
+
type="text"
|
|
313
|
+
class="search-input"
|
|
314
|
+
placeholder="搜索菜单..."
|
|
315
|
+
autofocus
|
|
316
|
+
@keyup.enter="searchResults[0] && handleSearchItemClick(searchResults[0].path)"
|
|
317
|
+
/>
|
|
318
|
+
<span class="search-shortcut">ESC 关闭</span>
|
|
319
|
+
</div>
|
|
320
|
+
<div v-if="searchResults.length > 0" class="search-results">
|
|
321
|
+
<div
|
|
322
|
+
v-for="(item, index) in searchResults"
|
|
323
|
+
:key="item.path"
|
|
324
|
+
:class="['search-result-item', { 'is-first': index === 0 }]"
|
|
325
|
+
@click="handleSearchItemClick(item.path)"
|
|
326
|
+
>
|
|
327
|
+
<Icon class="search-result-icon" :name="item.icon || 'file'" :size="20" />
|
|
328
|
+
<div class="search-result-info">
|
|
329
|
+
<span class="search-result-title">{{ item.title }}</span>
|
|
330
|
+
<span v-if="item.parentTitle" class="search-result-parent">{{ item.parentTitle }}</span>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
<div v-else-if="searchKeyword" class="search-empty">
|
|
335
|
+
未找到匹配的菜单
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
</Transition>
|
|
340
|
+
|
|
341
|
+
<!-- 换肤设置抽屉 -->
|
|
342
|
+
<Drawer
|
|
343
|
+
v-model="drawerVisible"
|
|
344
|
+
title="换肤设置"
|
|
345
|
+
direction="rtl"
|
|
346
|
+
size="320px"
|
|
347
|
+
>
|
|
348
|
+
<div class="settings-drawer">
|
|
349
|
+
<!-- 布局模式 -->
|
|
350
|
+
<div class="settings-section">
|
|
351
|
+
<div class="settings-title">布局模式</div>
|
|
352
|
+
<div class="settings-layout-options">
|
|
353
|
+
<div
|
|
354
|
+
v-for="option in layoutOptions"
|
|
355
|
+
:key="option.value"
|
|
356
|
+
:class="['layout-option', { 'is-active': currentLayout === option.value }]"
|
|
357
|
+
@click="handleLayoutChange(option.value)"
|
|
358
|
+
>
|
|
359
|
+
<div class="layout-option__preview">
|
|
360
|
+
<div v-if="option.value === 'sidebar'" class="layout-preview-sidebar">
|
|
361
|
+
<div class="preview-aside"></div>
|
|
362
|
+
<div class="preview-main">
|
|
363
|
+
<div class="preview-header"></div>
|
|
364
|
+
<div class="preview-content"></div>
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
<div v-else-if="option.value === 'top'" class="layout-preview-top">
|
|
368
|
+
<div class="preview-header-full"></div>
|
|
369
|
+
<div class="preview-content-full"></div>
|
|
370
|
+
</div>
|
|
371
|
+
<div v-else class="layout-preview-mix">
|
|
372
|
+
<div class="preview-header-mix">
|
|
373
|
+
<div class="preview-mix-left"></div>
|
|
374
|
+
</div>
|
|
375
|
+
<div class="preview-mix-body">
|
|
376
|
+
<div class="preview-mix-aside"></div>
|
|
377
|
+
<div class="preview-mix-content"></div>
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
<span class="layout-option__label">{{ option.label }}</span>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
|
|
386
|
+
<!-- 主题色 -->
|
|
387
|
+
<div class="settings-section">
|
|
388
|
+
<div class="settings-title">主题色</div>
|
|
389
|
+
<div class="settings-color-options">
|
|
390
|
+
<div
|
|
391
|
+
v-for="color in colorOptions"
|
|
392
|
+
:key="color.value"
|
|
393
|
+
:class="['color-option', { 'is-active': appStore.primaryColor === color.value }]"
|
|
394
|
+
:style="{ backgroundColor: color.value }"
|
|
395
|
+
:title="color.label"
|
|
396
|
+
@click="handleColorChange(color.value)"
|
|
397
|
+
>
|
|
398
|
+
<Icon v-if="appStore.primaryColor === color.value" name="check" :size="12" color="#fff" />
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
|
|
403
|
+
<!-- 功能开关 -->
|
|
404
|
+
<div class="settings-section">
|
|
405
|
+
<div class="settings-title">功能设置</div>
|
|
406
|
+
<div class="settings-switch-list">
|
|
407
|
+
<div class="settings-switch-item">
|
|
408
|
+
<span>灰色模式</span>
|
|
409
|
+
<div class="switch-wrapper" :class="{ 'is-checked': greyMode }" @click="handleGreyModeChange(!greyMode)">
|
|
410
|
+
<span class="switch-core"></span>
|
|
411
|
+
</div>
|
|
412
|
+
</div>
|
|
413
|
+
<div class="settings-switch-item">
|
|
414
|
+
<span>暗黑模式</span>
|
|
415
|
+
<div class="switch-wrapper" :class="{ 'is-checked': appStore.isDark }" @click="toggleTheme">
|
|
416
|
+
<span class="switch-core"></span>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
</div>
|
|
422
|
+
</Drawer>
|
|
183
423
|
</div>
|
|
184
424
|
</template>
|
|
185
425
|
|
|
@@ -190,6 +430,8 @@ onUnmounted(() => {
|
|
|
190
430
|
justify-content: space-between;
|
|
191
431
|
padding: 0 20px;
|
|
192
432
|
height: 100%;
|
|
433
|
+
background-color: var(--bg-color);
|
|
434
|
+
border-bottom: 1px solid var(--color-border-lighter);
|
|
193
435
|
|
|
194
436
|
&__left {
|
|
195
437
|
display: flex;
|
|
@@ -204,8 +446,8 @@ onUnmounted(() => {
|
|
|
204
446
|
align-items: center;
|
|
205
447
|
justify-content: center;
|
|
206
448
|
cursor: pointer;
|
|
207
|
-
font-size: 18px;
|
|
208
449
|
color: var(--color-text-regular);
|
|
450
|
+
transition: color 0.2s;
|
|
209
451
|
|
|
210
452
|
&:hover {
|
|
211
453
|
color: var(--color-primary);
|
|
@@ -216,6 +458,11 @@ onUnmounted(() => {
|
|
|
216
458
|
font-size: 14px;
|
|
217
459
|
color: var(--color-text-secondary);
|
|
218
460
|
|
|
461
|
+
.breadcrumb-separator {
|
|
462
|
+
margin: 0 8px;
|
|
463
|
+
color: var(--color-text-placeholder);
|
|
464
|
+
}
|
|
465
|
+
|
|
219
466
|
.is-current {
|
|
220
467
|
color: var(--color-text-primary);
|
|
221
468
|
font-weight: 500;
|
|
@@ -225,7 +472,7 @@ onUnmounted(() => {
|
|
|
225
472
|
&__right {
|
|
226
473
|
display: flex;
|
|
227
474
|
align-items: center;
|
|
228
|
-
gap:
|
|
475
|
+
gap: 8px;
|
|
229
476
|
}
|
|
230
477
|
|
|
231
478
|
&__action {
|
|
@@ -236,15 +483,18 @@ onUnmounted(() => {
|
|
|
236
483
|
justify-content: center;
|
|
237
484
|
cursor: pointer;
|
|
238
485
|
border-radius: var(--border-radius-base);
|
|
239
|
-
|
|
486
|
+
color: var(--color-text-regular);
|
|
487
|
+
transition: all 0.2s;
|
|
240
488
|
|
|
241
489
|
&:hover {
|
|
242
490
|
background-color: var(--color-fill);
|
|
491
|
+
color: var(--color-primary);
|
|
243
492
|
}
|
|
244
493
|
}
|
|
245
494
|
|
|
246
495
|
&__user {
|
|
247
496
|
position: relative;
|
|
497
|
+
margin-left: 8px;
|
|
248
498
|
|
|
249
499
|
&-trigger {
|
|
250
500
|
display: flex;
|
|
@@ -263,6 +513,10 @@ onUnmounted(() => {
|
|
|
263
513
|
&-name {
|
|
264
514
|
font-size: 14px;
|
|
265
515
|
color: var(--color-text-primary);
|
|
516
|
+
max-width: 100px;
|
|
517
|
+
overflow: hidden;
|
|
518
|
+
text-overflow: ellipsis;
|
|
519
|
+
white-space: nowrap;
|
|
266
520
|
}
|
|
267
521
|
|
|
268
522
|
&-arrow {
|
|
@@ -369,9 +623,323 @@ onUnmounted(() => {
|
|
|
369
623
|
}
|
|
370
624
|
}
|
|
371
625
|
}
|
|
626
|
+
}
|
|
372
627
|
|
|
373
|
-
|
|
374
|
-
|
|
628
|
+
&__search-modal {
|
|
629
|
+
position: fixed;
|
|
630
|
+
top: 0;
|
|
631
|
+
left: 0;
|
|
632
|
+
right: 0;
|
|
633
|
+
bottom: 0;
|
|
634
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
635
|
+
display: flex;
|
|
636
|
+
align-items: flex-start;
|
|
637
|
+
justify-content: center;
|
|
638
|
+
padding-top: 100px;
|
|
639
|
+
z-index: 200;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// 搜索容器
|
|
644
|
+
.search-container {
|
|
645
|
+
width: 600px;
|
|
646
|
+
max-width: 90vw;
|
|
647
|
+
background-color: var(--bg-color);
|
|
648
|
+
border-radius: var(--border-radius-large);
|
|
649
|
+
box-shadow: var(--box-shadow-dark);
|
|
650
|
+
overflow: hidden;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.search-input-wrapper {
|
|
654
|
+
display: flex;
|
|
655
|
+
align-items: center;
|
|
656
|
+
padding: 16px 20px;
|
|
657
|
+
border-bottom: 1px solid var(--color-border-lighter);
|
|
658
|
+
|
|
659
|
+
.search-icon {
|
|
660
|
+
color: var(--color-text-secondary);
|
|
661
|
+
margin-right: 12px;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.search-input {
|
|
665
|
+
flex: 1;
|
|
666
|
+
font-size: 16px;
|
|
667
|
+
color: var(--color-text-primary);
|
|
668
|
+
background: transparent;
|
|
669
|
+
border: none;
|
|
670
|
+
outline: none;
|
|
671
|
+
|
|
672
|
+
&::placeholder {
|
|
673
|
+
color: var(--color-text-placeholder);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
.search-shortcut {
|
|
678
|
+
font-size: 12px;
|
|
679
|
+
color: var(--color-text-secondary);
|
|
680
|
+
padding: 4px 8px;
|
|
681
|
+
background-color: var(--color-fill);
|
|
682
|
+
border-radius: var(--border-radius-base);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.search-results {
|
|
687
|
+
max-height: 400px;
|
|
688
|
+
overflow-y: auto;
|
|
689
|
+
padding: 8px 0;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.search-result-item {
|
|
693
|
+
display: flex;
|
|
694
|
+
align-items: center;
|
|
695
|
+
gap: 12px;
|
|
696
|
+
padding: 12px 20px;
|
|
697
|
+
cursor: pointer;
|
|
698
|
+
transition: background-color 0.2s;
|
|
699
|
+
|
|
700
|
+
&:hover {
|
|
701
|
+
background-color: var(--color-fill);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
&.is-first {
|
|
705
|
+
background-color: var(--color-primary-light-9);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
.search-result-icon {
|
|
709
|
+
color: var(--color-text-secondary);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
.search-result-info {
|
|
713
|
+
flex: 1;
|
|
714
|
+
display: flex;
|
|
715
|
+
flex-direction: column;
|
|
716
|
+
gap: 2px;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.search-result-title {
|
|
720
|
+
font-size: 14px;
|
|
721
|
+
color: var(--color-text-primary);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
.search-result-parent {
|
|
725
|
+
font-size: 12px;
|
|
726
|
+
color: var(--color-text-secondary);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.search-empty {
|
|
731
|
+
padding: 40px 20px;
|
|
732
|
+
text-align: center;
|
|
733
|
+
color: var(--color-text-secondary);
|
|
734
|
+
font-size: 14px;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// 设置抽屉内容
|
|
738
|
+
.settings-drawer {
|
|
739
|
+
.settings-section {
|
|
740
|
+
margin-bottom: 24px;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
.settings-title {
|
|
744
|
+
font-size: 14px;
|
|
745
|
+
font-weight: 500;
|
|
746
|
+
color: var(--color-text-primary);
|
|
747
|
+
margin-bottom: 12px;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
.settings-layout-options {
|
|
751
|
+
display: flex;
|
|
752
|
+
gap: 12px;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
.layout-option {
|
|
756
|
+
flex: 1;
|
|
757
|
+
display: flex;
|
|
758
|
+
flex-direction: column;
|
|
759
|
+
align-items: center;
|
|
760
|
+
gap: 8px;
|
|
761
|
+
padding: 12px;
|
|
762
|
+
border: 1px solid var(--color-border);
|
|
763
|
+
border-radius: var(--border-radius-base);
|
|
764
|
+
cursor: pointer;
|
|
765
|
+
transition: all 0.2s;
|
|
766
|
+
|
|
767
|
+
&:hover {
|
|
768
|
+
border-color: var(--color-primary-light-5);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
&.is-active {
|
|
772
|
+
border-color: var(--color-primary);
|
|
773
|
+
background-color: var(--color-primary-light-9);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
&__preview {
|
|
777
|
+
width: 48px;
|
|
778
|
+
height: 36px;
|
|
779
|
+
border: 1px solid var(--color-border-light);
|
|
780
|
+
border-radius: 2px;
|
|
781
|
+
overflow: hidden;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
&__label {
|
|
785
|
+
font-size: 12px;
|
|
786
|
+
color: var(--color-text-regular);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// 布局预览样式
|
|
791
|
+
.layout-preview-sidebar {
|
|
792
|
+
display: flex;
|
|
793
|
+
height: 100%;
|
|
794
|
+
|
|
795
|
+
.preview-aside {
|
|
796
|
+
width: 25%;
|
|
797
|
+
height: 100%;
|
|
798
|
+
background-color: var(--color-primary-light-7);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.preview-main {
|
|
802
|
+
flex: 1;
|
|
803
|
+
display: flex;
|
|
804
|
+
flex-direction: column;
|
|
805
|
+
|
|
806
|
+
.preview-header {
|
|
807
|
+
height: 20%;
|
|
808
|
+
background-color: var(--color-border-light);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
.preview-content {
|
|
812
|
+
flex: 1;
|
|
813
|
+
background-color: var(--bg-color-page);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
.layout-preview-top {
|
|
819
|
+
display: flex;
|
|
820
|
+
flex-direction: column;
|
|
821
|
+
height: 100%;
|
|
822
|
+
|
|
823
|
+
.preview-header-full {
|
|
824
|
+
height: 25%;
|
|
825
|
+
background-color: var(--color-primary-light-7);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
.preview-content-full {
|
|
829
|
+
flex: 1;
|
|
830
|
+
background-color: var(--bg-color-page);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
.layout-preview-mix {
|
|
835
|
+
display: flex;
|
|
836
|
+
flex-direction: column;
|
|
837
|
+
height: 100%;
|
|
838
|
+
|
|
839
|
+
.preview-header-mix {
|
|
840
|
+
height: 25%;
|
|
841
|
+
background-color: var(--color-primary-light-7);
|
|
842
|
+
display: flex;
|
|
843
|
+
|
|
844
|
+
.preview-mix-left {
|
|
845
|
+
width: 30%;
|
|
846
|
+
background-color: var(--color-primary);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.preview-mix-body {
|
|
851
|
+
flex: 1;
|
|
852
|
+
display: flex;
|
|
853
|
+
|
|
854
|
+
.preview-mix-aside {
|
|
855
|
+
width: 25%;
|
|
856
|
+
background-color: var(--color-primary-light-8);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
.preview-mix-content {
|
|
860
|
+
flex: 1;
|
|
861
|
+
background-color: var(--bg-color-page);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
.settings-color-options {
|
|
867
|
+
display: flex;
|
|
868
|
+
gap: 12px;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
.color-option {
|
|
872
|
+
width: 24px;
|
|
873
|
+
height: 24px;
|
|
874
|
+
border-radius: 4px;
|
|
875
|
+
cursor: pointer;
|
|
876
|
+
display: flex;
|
|
877
|
+
align-items: center;
|
|
878
|
+
justify-content: center;
|
|
879
|
+
transition: transform 0.2s;
|
|
880
|
+
|
|
881
|
+
&:hover {
|
|
882
|
+
transform: scale(1.1);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
&.is-active {
|
|
886
|
+
box-shadow: 0 0 0 2px var(--bg-color), 0 0 0 4px var(--color-primary);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
.settings-switch-list {
|
|
891
|
+
display: flex;
|
|
892
|
+
flex-direction: column;
|
|
893
|
+
gap: 12px;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
.settings-switch-item {
|
|
897
|
+
display: flex;
|
|
898
|
+
align-items: center;
|
|
899
|
+
justify-content: space-between;
|
|
900
|
+
|
|
901
|
+
span {
|
|
902
|
+
font-size: 14px;
|
|
903
|
+
color: var(--color-text-regular);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
.switch-wrapper {
|
|
908
|
+
width: 44px;
|
|
909
|
+
height: 22px;
|
|
910
|
+
display: flex;
|
|
911
|
+
align-items: center;
|
|
912
|
+
cursor: pointer;
|
|
913
|
+
|
|
914
|
+
.switch-core {
|
|
915
|
+
width: 100%;
|
|
916
|
+
height: 100%;
|
|
917
|
+
border-radius: 11px;
|
|
918
|
+
background-color: var(--color-border);
|
|
919
|
+
position: relative;
|
|
920
|
+
transition: background-color 0.2s;
|
|
921
|
+
|
|
922
|
+
&::after {
|
|
923
|
+
content: '';
|
|
924
|
+
position: absolute;
|
|
925
|
+
top: 2px;
|
|
926
|
+
left: 2px;
|
|
927
|
+
width: 18px;
|
|
928
|
+
height: 18px;
|
|
929
|
+
background-color: #fff;
|
|
930
|
+
border-radius: 50%;
|
|
931
|
+
transition: left 0.2s;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
&.is-checked {
|
|
936
|
+
.switch-core {
|
|
937
|
+
background-color: var(--color-primary);
|
|
938
|
+
|
|
939
|
+
&::after {
|
|
940
|
+
left: 24px;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
375
943
|
}
|
|
376
944
|
}
|
|
377
945
|
}
|
|
@@ -387,4 +955,20 @@ onUnmounted(() => {
|
|
|
387
955
|
opacity: 0;
|
|
388
956
|
transform: translateY(-10px);
|
|
389
957
|
}
|
|
958
|
+
|
|
959
|
+
// 搜索弹窗动画
|
|
960
|
+
.search-enter-active,
|
|
961
|
+
.search-leave-active {
|
|
962
|
+
transition: all 0.2s ease;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.search-enter-from,
|
|
966
|
+
.search-leave-to {
|
|
967
|
+
opacity: 0;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// 灰色模式
|
|
971
|
+
:root.grey-mode {
|
|
972
|
+
filter: grayscale(100%);
|
|
973
|
+
}
|
|
390
974
|
</style>
|