Refactor layout components for improved readability and consistency
This commit is contained in:
@@ -1,15 +1,25 @@
|
||||
<template>
|
||||
<v-app v-bind="$attrs" class="sk-admin-layout">
|
||||
<v-navigation-drawer
|
||||
v-model="drawer" class="sk-admin-drawer" color="surface" :rail="isRail"
|
||||
:rail-width="railWidth" :temporary="isMobile" :width="drawerWidth">
|
||||
v-model="drawer"
|
||||
class="sk-admin-drawer"
|
||||
color="surface"
|
||||
:rail="isRail"
|
||||
:rail-width="railWidth"
|
||||
:temporary="isMobile"
|
||||
:width="drawerWidth"
|
||||
>
|
||||
<template #prepend>
|
||||
|
||||
<!-- Sidebar Title -->
|
||||
<v-card class="sidebar-header d-flex align-center pa-0 pl-3 py-2" flat>
|
||||
<v-btn
|
||||
:aria-label="sidebarToggleLabel" color="grey" :icon="isRail ? mdiMenuOpen : mdiMenu" size="32"
|
||||
variant="text" @click="toggleSidebar" />
|
||||
:aria-label="sidebarToggleLabel"
|
||||
color="grey"
|
||||
:icon="isRail ? mdiMenuOpen : mdiMenu"
|
||||
size="32"
|
||||
variant="text"
|
||||
@click="toggleSidebar"
|
||||
/>
|
||||
<v-card-text v-if="!isRail" class="sidebar-title flex-grow-1 py-0">
|
||||
<slot name="title">
|
||||
<div class="text-subtitle-1 font-weight-bold text-on-surface">
|
||||
@@ -24,7 +34,11 @@ v-model="drawer" class="sk-admin-drawer" color="surface" :rail="isRail"
|
||||
<v-divider />
|
||||
|
||||
<!-- User Info -->
|
||||
<v-card v-if="features.showUserInfo" class="user-info d-flex align-center pa-0 pl-3 py-2" flat>
|
||||
<v-card
|
||||
v-if="features.showUserInfo"
|
||||
class="user-info d-flex align-center pa-0 pl-3 py-2"
|
||||
flat
|
||||
>
|
||||
<v-avatar class="user-avatar" color="primary" size="32" variant="tonal">
|
||||
<span class="text-subtitle-2 font-weight-bold">{{ userProfile.avatarText }}</span>
|
||||
</v-avatar>
|
||||
@@ -39,17 +53,34 @@ v-model="drawer" class="sk-admin-drawer" color="surface" :rail="isRail"
|
||||
</v-card>
|
||||
<v-divider />
|
||||
|
||||
<v-sheet v-if="isMobile" class="mobile-menu-subheader d-flex flex-column align-stretch ga-2 px-3 py-2">
|
||||
<v-sheet
|
||||
v-if="isMobile"
|
||||
class="mobile-menu-subheader d-flex flex-column align-stretch ga-2 px-3 py-2"
|
||||
>
|
||||
<v-btn
|
||||
v-if="features.showFavorites" class="justify-start text-none"
|
||||
color="primary" rounded="pill" size="small" :variant="mobileFavoritesPanel ? 'flat' : 'outlined'"
|
||||
@click="openMobileFavoritesPanel">
|
||||
<span class="text-on-secondary text-caption font-weight-medium">{{ favoritesConfig.label }}</span>
|
||||
v-if="features.showFavorites"
|
||||
class="justify-start text-none"
|
||||
color="primary"
|
||||
rounded="pill"
|
||||
size="small"
|
||||
:variant="mobileFavoritesPanel ? 'flat' : 'outlined'"
|
||||
@click="openMobileFavoritesPanel"
|
||||
>
|
||||
<span class="text-on-secondary text-caption font-weight-medium">{{
|
||||
favoritesConfig.label
|
||||
}}</span>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-for="step in mobileMenuLevels" :key="`mobile-level-${step.level}`"
|
||||
block class="justify-start text-none" :color="getMobileMenuBtnColor(step.level)"
|
||||
rounded="pill" size="small" :variant="getMobileMenuBtnVariant(step.level)" @click="goToMobileLevel(step.level)">
|
||||
v-for="step in mobileMenuLevels"
|
||||
:key="`mobile-level-${step.level}`"
|
||||
block
|
||||
class="justify-start text-none"
|
||||
:color="getMobileMenuBtnColor(step.level)"
|
||||
rounded="pill"
|
||||
size="small"
|
||||
:variant="getMobileMenuBtnVariant(step.level)"
|
||||
@click="goToMobileLevel(step.level)"
|
||||
>
|
||||
{{ step.title }}
|
||||
</v-btn>
|
||||
</v-sheet>
|
||||
@@ -58,47 +89,76 @@ v-for="step in mobileMenuLevels" :key="`mobile-level-${step.level}`"
|
||||
<!-- 桌面板選單 -->
|
||||
<template v-if="!isMobile">
|
||||
<SkAdminDrawerDesktopMenu
|
||||
v-model:opened="opened" :is-shrink="isRail" :menu-items="menuItems"
|
||||
@select="handleSelect" @unshrink="handleUnshrink" />
|
||||
v-model:opened="opened"
|
||||
:is-shrink="isRail"
|
||||
:menu-items="menuItems"
|
||||
@select="handleSelect"
|
||||
@unshrink="handleUnshrink"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 行動版選單 -->
|
||||
<template v-if="isMobile">
|
||||
<SkAdminDrawerMobileFavoritesPanel
|
||||
v-if="features.showFavorites && mobileFavoritesPanel"
|
||||
:favorite-items="favoriteItems" @select="onSelectFavorite" />
|
||||
v-if="features.showFavorites && mobileFavoritesPanel"
|
||||
:favorite-items="favoriteItems"
|
||||
@select="onSelectFavorite"
|
||||
/>
|
||||
<SkAdminDrawerMobileMenuPanel
|
||||
v-else :mobile-current-items="mobileCurrentItems"
|
||||
@item-click="onMobileMenuClick" />
|
||||
v-else
|
||||
:mobile-current-items="mobileCurrentItems"
|
||||
@item-click="onMobileMenuClick"
|
||||
/>
|
||||
</template>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-app-bar ref="appBarRef" class="" height="auto">
|
||||
<v-row class="flex-column" no-gutters>
|
||||
|
||||
<SkAdminAppBarTopCol
|
||||
:features="features" :is-mobile="isMobile" :logout-label="logoutLabel"
|
||||
:search-config="searchConfig" :search-value="searchValue" :show-breadcrumb-bar="showBreadcrumbBar"
|
||||
:show-favorites-bar="showFavoritesBar" :theme-toggle-label="themeToggleLabel" :toolbar-actions="toolbarActions"
|
||||
:toolbar-counts="toolbarCounts" @action="handleAction"
|
||||
@logout="emitLogout" @search="triggerSearch" @toggle-drawer="drawer = !drawer"
|
||||
@toggle-theme="toggleTheme" @update:search-value="searchValue = $event" @update:show-breadcrumb-bar="showBreadcrumbBar = $event"
|
||||
@update:show-favorites-bar="showFavoritesBar = $event">
|
||||
:features="features"
|
||||
:is-mobile="isMobile"
|
||||
:logout-label="logoutLabel"
|
||||
:search-config="searchConfig"
|
||||
:search-value="searchValue"
|
||||
:show-breadcrumb-bar="showBreadcrumbBar"
|
||||
:show-favorites-bar="showFavoritesBar"
|
||||
:theme-toggle-label="themeToggleLabel"
|
||||
:toolbar-actions="toolbarActions"
|
||||
:toolbar-counts="toolbarCounts"
|
||||
@action="handleAction"
|
||||
@logout="emitLogout"
|
||||
@search="triggerSearch"
|
||||
@toggle-drawer="drawer = !drawer"
|
||||
@toggle-theme="toggleTheme"
|
||||
@update:search-value="searchValue = $event"
|
||||
@update:show-breadcrumb-bar="showBreadcrumbBar = $event"
|
||||
@update:show-favorites-bar="showFavoritesBar = $event"
|
||||
>
|
||||
<template v-if="$slots.actions" #actions>
|
||||
<slot name="actions"></slot>
|
||||
</template>
|
||||
</SkAdminAppBarTopCol>
|
||||
|
||||
<SkAdminAppBarFavoritesCol
|
||||
:favorite-items="favoriteItems" :favorites-config="favoritesConfig" :features="features"
|
||||
:is-mobile="isMobile" :show-favorites-bar="showFavoritesBar" @add-favorite="emitAddFavorite"
|
||||
@remove-favorite="emitRemoveFavorite" @select="handleSelect"
|
||||
@toggle-favorites-bar="toggleFavoritesBar" />
|
||||
:favorite-items="favoriteItems"
|
||||
:favorites-config="favoritesConfig"
|
||||
:features="features"
|
||||
:is-mobile="isMobile"
|
||||
:show-favorites-bar="showFavoritesBar"
|
||||
@add-favorite="emitAddFavorite"
|
||||
@remove-favorite="emitRemoveFavorite"
|
||||
@select="handleSelect"
|
||||
@toggle-favorites-bar="toggleFavoritesBar"
|
||||
/>
|
||||
|
||||
<SkAdminAppBarBreadcrumbCol
|
||||
:breadcrumb-items="breadcrumbItems" :features="features" :is-mobile="isMobile"
|
||||
:show-breadcrumb-bar="showBreadcrumbBar" :show-favorites-bar="showFavoritesBar"
|
||||
@toggle-favorites-bar="toggleFavoritesBar">
|
||||
:breadcrumb-items="breadcrumbItems"
|
||||
:features="features"
|
||||
:is-mobile="isMobile"
|
||||
:show-breadcrumb-bar="showBreadcrumbBar"
|
||||
:show-favorites-bar="showFavoritesBar"
|
||||
@toggle-favorites-bar="toggleFavoritesBar"
|
||||
>
|
||||
<template v-if="$slots['breadcrumb-actions']" #breadcrumb-actions>
|
||||
<slot name="breadcrumb-actions"></slot>
|
||||
</template>
|
||||
@@ -125,8 +185,12 @@ v-else :mobile-current-items="mobileCurrentItems"
|
||||
<v-card-title class="text-subtitle-2">操作說明</v-card-title>
|
||||
<template #append>
|
||||
<v-btn
|
||||
aria-label="關閉說明" :icon="mdiClose" size="small" variant="text"
|
||||
@click="helpWidgetVisible = false" />
|
||||
aria-label="關閉說明"
|
||||
:icon="mdiClose"
|
||||
size="small"
|
||||
variant="text"
|
||||
@click="helpWidgetVisible = false"
|
||||
/>
|
||||
</template>
|
||||
</v-card-item>
|
||||
<v-divider />
|
||||
@@ -146,11 +210,6 @@ aria-label="關閉說明" :icon="mdiClose" size="small" variant="text"
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { mdiClose, mdiHelpCircleOutline, mdiHome, mdiMenu, mdiMenuOpen } from '@mdi/js'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useAdminLayoutState } from '@/composables/layout/useAdminLayoutState'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useThemeToggle } from '@/composables/layout/useThemeToggle'
|
||||
import type {
|
||||
AdminLayoutActionType,
|
||||
AdminLayoutBreadcrumbConfig,
|
||||
@@ -164,6 +223,11 @@ import type {
|
||||
AdminLayoutToolbarCounts,
|
||||
AdminLayoutUserProfile,
|
||||
} from './sk-admin-layout/types'
|
||||
import { mdiClose, mdiHelpCircleOutline, mdiHome, mdiMenu, mdiMenuOpen } from '@mdi/js'
|
||||
import { computed, ref, toRef } from 'vue'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useAdminLayoutState } from '@/composables/layout/useAdminLayoutState'
|
||||
import { useThemeToggle } from '@/composables/layout/useThemeToggle'
|
||||
import SkAdminAppBarBreadcrumbCol from './sk-admin-layout/SkAdminAppBarBreadcrumbCol.vue'
|
||||
import SkAdminAppBarFavoritesCol from './sk-admin-layout/SkAdminAppBarFavoritesCol.vue'
|
||||
import SkAdminAppBarTopCol from './sk-admin-layout/SkAdminAppBarTopCol.vue'
|
||||
@@ -176,7 +240,7 @@ const emit = defineEmits<{
|
||||
select: [item: AdminLayoutMenuItem]
|
||||
search: [keyword: string]
|
||||
action: [type: AdminLayoutActionType]
|
||||
'toggle-sidebar': [payload: { drawer: boolean, rail: boolean }]
|
||||
'toggle-sidebar': [payload: { drawer: boolean; rail: boolean }]
|
||||
'toggle-theme': [themeName: string]
|
||||
'add-favorite': []
|
||||
'remove-favorite': [item: AdminLayoutMenuItem]
|
||||
@@ -231,38 +295,36 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
sidebarToggleLabel: '切換側欄',
|
||||
favoriteHeaderLabel: '我的最愛',
|
||||
favoriteItems: () => [],
|
||||
menuItems: () => [
|
||||
{ title: '首頁', path: '/' },
|
||||
],
|
||||
menuItems: () => [{ title: '首頁', path: '/' }],
|
||||
userProfile: () => ({
|
||||
name: '王小明',
|
||||
role: '資訊工程系 - 學生',
|
||||
avatarText: '王',
|
||||
}),
|
||||
name: '王小明',
|
||||
role: '資訊工程系 - 學生',
|
||||
avatarText: '王',
|
||||
}),
|
||||
searchConfig: () => ({
|
||||
placeholder: '搜尋功能名稱... (試試「成績」、「選課」、「請假」)',
|
||||
label: '搜尋',
|
||||
}),
|
||||
placeholder: '搜尋功能名稱... (試試「成績」、「選課」、「請假」)',
|
||||
label: '搜尋',
|
||||
}),
|
||||
toolbarActions: () => ({
|
||||
notificationsLabel: '通知',
|
||||
messagesLabel: '訊息',
|
||||
helpLabel: '說明',
|
||||
settingsLabel: '設定',
|
||||
}),
|
||||
notificationsLabel: '通知',
|
||||
messagesLabel: '訊息',
|
||||
helpLabel: '說明',
|
||||
settingsLabel: '設定',
|
||||
}),
|
||||
toolbarCounts: () => ({
|
||||
notifications: 0,
|
||||
messages: 0,
|
||||
}),
|
||||
notifications: 0,
|
||||
messages: 0,
|
||||
}),
|
||||
favoritesConfig: () => ({
|
||||
label: '常用',
|
||||
addLabel: '新增常用',
|
||||
showAdd: false,
|
||||
}),
|
||||
label: '常用',
|
||||
addLabel: '新增常用',
|
||||
showAdd: false,
|
||||
}),
|
||||
breadcrumbConfig: () => ({
|
||||
homeLabel: '首頁',
|
||||
homeDisabled: true,
|
||||
homeIcon: mdiHome,
|
||||
}),
|
||||
homeLabel: '首頁',
|
||||
homeDisabled: true,
|
||||
homeIcon: mdiHome,
|
||||
}),
|
||||
breadcrumbItems: () => [],
|
||||
favoritesBarVisible: null,
|
||||
breadcrumbBarVisible: null,
|
||||
@@ -276,9 +338,9 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
showUserInfo: true,
|
||||
}),
|
||||
drawerConfig: () => ({
|
||||
width: 280,
|
||||
railWidth: 56,
|
||||
}),
|
||||
width: 280,
|
||||
railWidth: 56,
|
||||
}),
|
||||
})
|
||||
|
||||
// Feature toggle: UI 區塊顯示
|
||||
@@ -339,12 +401,12 @@ const {
|
||||
emitUpdateIsRail: (value) => emit('update:isRail', value),
|
||||
favoritesBarVisible: props.favoritesBarVisible,
|
||||
isMobile,
|
||||
isRail: props.isRail,
|
||||
isRail: toRef(props, 'isRail'), // 傳 Ref 確保 composable getter 能隨 prop 更新
|
||||
menuItems: props.menuItems,
|
||||
onToggleSidebar: (payload) => emit('toggle-sidebar', payload),
|
||||
})
|
||||
|
||||
function toggleTheme () {
|
||||
function toggleTheme() {
|
||||
const next = switchTheme()
|
||||
if (!next) return
|
||||
emit('toggle-theme', next)
|
||||
@@ -352,7 +414,7 @@ function toggleTheme () {
|
||||
|
||||
const emitLogout = () => emit('logout')
|
||||
|
||||
function handleAction (type: AdminLayoutActionType) {
|
||||
function handleAction(type: AdminLayoutActionType) {
|
||||
if (type === 'help') {
|
||||
helpWidgetVisible.value = true
|
||||
}
|
||||
@@ -360,41 +422,39 @@ function handleAction (type: AdminLayoutActionType) {
|
||||
}
|
||||
|
||||
// 以按鈕或 Enter 觸發搜尋,避免每個字都觸發
|
||||
function triggerSearch () {
|
||||
function triggerSearch() {
|
||||
const keyword = searchValue.value
|
||||
emit('search', keyword)
|
||||
// 觸發後清空欄位,避免彈窗出現仍保留文字
|
||||
searchValue.value = ''
|
||||
}
|
||||
|
||||
function emitAddFavorite () {
|
||||
function emitAddFavorite() {
|
||||
emit('add-favorite')
|
||||
}
|
||||
|
||||
function emitRemoveFavorite (item: AdminLayoutMenuItem) {
|
||||
function emitRemoveFavorite(item: AdminLayoutMenuItem) {
|
||||
emit('remove-favorite', item)
|
||||
}
|
||||
|
||||
function onSelectFavorite (item: AdminLayoutMenuItem) {
|
||||
function onSelectFavorite(item: AdminLayoutMenuItem) {
|
||||
handleSelectFavorite(item, handleSelect)
|
||||
}
|
||||
|
||||
function onMobileMenuClick (item: AdminLayoutMenuItem) {
|
||||
function onMobileMenuClick(item: AdminLayoutMenuItem) {
|
||||
handleMobileMenuClick(item, handleSelect)
|
||||
}
|
||||
|
||||
function handleSelect (item: AdminLayoutMenuItem) {
|
||||
function handleSelect(item: AdminLayoutMenuItem) {
|
||||
emit('select', item)
|
||||
if (isMobile.value) drawer.value = false
|
||||
}
|
||||
|
||||
function getMobileMenuBtnVariant (level: number) {
|
||||
return !mobileFavoritesPanel.value && level === mobileCurrentLevel.value
|
||||
? 'flat'
|
||||
: 'outlined'
|
||||
function getMobileMenuBtnVariant(level: number) {
|
||||
return !mobileFavoritesPanel.value && level === mobileCurrentLevel.value ? 'flat' : 'outlined'
|
||||
}
|
||||
|
||||
function getMobileMenuBtnColor (level: number) {
|
||||
function getMobileMenuBtnColor(level: number) {
|
||||
return level === mobileCurrentLevel.value ? 'primary' : 'secondary'
|
||||
}
|
||||
</script>
|
||||
@@ -433,8 +493,6 @@ function getMobileMenuBtnColor (level: number) {
|
||||
background: rgb(var(--v-theme-background));
|
||||
}
|
||||
|
||||
|
||||
|
||||
.search-input-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@@ -515,7 +573,9 @@ function getMobileMenuBtnColor (level: number) {
|
||||
/* 確保 v-list-group 的 activator 也有 hover 效果 */
|
||||
:deep(.v-list-group > .v-list-item) {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease;
|
||||
}
|
||||
|
||||
:deep(.v-list-group > .v-list-item:hover) {
|
||||
@@ -525,7 +585,9 @@ function getMobileMenuBtnColor (level: number) {
|
||||
|
||||
/* 為所有 v-list-item 加上 transition */
|
||||
:deep(.v-list-item) {
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease;
|
||||
}
|
||||
|
||||
/* 確保滾軸邊距 */
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { AdminLayoutFavoritesConfig, AdminLayoutMenuItem } from './sk-admin-layout/types'
|
||||
import { computed } from 'vue'
|
||||
import SKAdminLayout from './SKAdminLayout.vue'
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -27,8 +27,8 @@ v-if="features.showFavorites && !showFavoritesBar" class="mr-2" color="primary"
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { mdiChevronRight } from '@mdi/js'
|
||||
import type { AdminLayoutBreadcrumbItem, AdminLayoutFeatures } from './types'
|
||||
import { mdiChevronRight } from '@mdi/js'
|
||||
|
||||
interface Props {
|
||||
features?: AdminLayoutFeatures
|
||||
|
||||
@@ -29,8 +29,8 @@ v-if="favoritesConfig.showAdd" class="favorite-add" color="primary" size="small"
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { mdiEyeOff, mdiPlus } from '@mdi/js'
|
||||
import type { AdminLayoutFavoritesConfig, AdminLayoutFeatures, AdminLayoutMenuItem } from './types'
|
||||
import { mdiEyeOff, mdiPlus } from '@mdi/js'
|
||||
|
||||
interface Props {
|
||||
features?: AdminLayoutFeatures
|
||||
|
||||
@@ -23,9 +23,9 @@ v-model="searchValueModel" :aria-label="searchConfig.label" class="search-input"
|
||||
|
||||
<!-- 通知 -->
|
||||
<v-tooltip location="bottom" :text="toolbarActions.notificationsLabel">
|
||||
<template #activator="{ props }">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-btn
|
||||
v-bind="props" :aria-label="toolbarActions.notificationsLabel" icon size="small" variant="text"
|
||||
v-bind="activatorProps" :aria-label="toolbarActions.notificationsLabel" icon size="small" variant="text"
|
||||
@click="emit('action', 'notifications')">
|
||||
<v-badge
|
||||
v-if="toolbarCounts.notifications" color="error" :content="toolbarCounts.notifications"
|
||||
@@ -39,9 +39,9 @@ v-if="toolbarCounts.notifications" color="error" :content="toolbarCounts.notific
|
||||
|
||||
<!-- 訊息 -->
|
||||
<v-tooltip location="bottom" :text="toolbarActions.messagesLabel">
|
||||
<template #activator="{ props }">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-btn
|
||||
v-bind="props" :aria-label="toolbarActions.messagesLabel" icon size="small" variant="text"
|
||||
v-bind="activatorProps" :aria-label="toolbarActions.messagesLabel" icon size="small" variant="text"
|
||||
@click="emit('action', 'messages')">
|
||||
<v-badge
|
||||
v-if="toolbarCounts.messages" color="warning" :content="toolbarCounts.messages" offset-x="4"
|
||||
@@ -55,9 +55,9 @@ v-if="toolbarCounts.messages" color="warning" :content="toolbarCounts.messages"
|
||||
|
||||
<!-- 說明 -->
|
||||
<v-tooltip v-if="false" location="bottom" :text="toolbarActions.helpLabel">
|
||||
<template #activator="{ props }">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-btn
|
||||
v-bind="props" :aria-label="toolbarActions.helpLabel" icon size="small" variant="text"
|
||||
v-bind="activatorProps" :aria-label="toolbarActions.helpLabel" icon size="small" variant="text"
|
||||
@click="emit('action', 'help')">
|
||||
<v-icon :icon="mdiHelp" />
|
||||
</v-btn>
|
||||
@@ -98,16 +98,16 @@ v-bind="{ ...menuProps, ...tooltipProps }" :aria-label="toolbarActions.settingsL
|
||||
|
||||
<!-- 登出 -->
|
||||
<v-tooltip location="bottom" :text="logoutLabel">
|
||||
<template #activator="{ props }">
|
||||
<v-btn v-bind="props" :aria-label="logoutLabel" icon size="small" variant="text" @click="emit('logout')">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-btn v-bind="activatorProps" :aria-label="logoutLabel" icon size="small" variant="text" @click="emit('logout')">
|
||||
<v-icon :icon="mdiLogout" />
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
|
||||
<v-tooltip v-if="features.showThemeToggle" location="bottom" :text="themeToggleLabel">
|
||||
<template #activator="{ props }">
|
||||
<v-btn v-bind="props" :aria-label="themeToggleLabel" icon variant="text" @click="emit('toggle-theme')">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-btn v-bind="activatorProps" :aria-label="themeToggleLabel" icon variant="text" @click="emit('toggle-theme')">
|
||||
<v-icon :icon="mdiPaletteOutline" />
|
||||
</v-btn>
|
||||
</template>
|
||||
@@ -118,9 +118,9 @@ v-bind="{ ...menuProps, ...tooltipProps }" :aria-label="toolbarActions.settingsL
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { AdminLayoutActionType, AdminLayoutFeatures, AdminLayoutSearchConfig, AdminLayoutToolbarActions, AdminLayoutToolbarCounts } from './types'
|
||||
import { mdiBellOutline, mdiCogOutline, mdiHelp, mdiLogout, mdiMagnify, mdiMenu, mdiMessageTextOutline, mdiPaletteOutline } from '@mdi/js'
|
||||
import { computed } from 'vue'
|
||||
import type { AdminLayoutActionType, AdminLayoutFeatures, AdminLayoutSearchConfig, AdminLayoutToolbarActions, AdminLayoutToolbarCounts } from './types'
|
||||
|
||||
interface Props {
|
||||
isMobile?: boolean
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<v-list v-model:opened="openedModel" color="primary" density="compact" prepend-gap="8">
|
||||
<template v-for="item in menuItems" :key="item.path ?? item.title">
|
||||
<v-list-group v-if="item.subItems?.length" class="menu-group" :value="`menu:${item.path ?? item.title}`">
|
||||
<template #activator="{ props }">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-list-item
|
||||
v-bind="isShrink ? undefined : props" :class="{ 'px-0': isShrink }"
|
||||
v-bind="isShrink ? undefined : activatorProps" :class="{ 'px-0': isShrink }"
|
||||
:link="isNavigable(item) && !!item.path" :to="isNavigable(item) ? item.path : undefined" @click="emitSelect(item)">
|
||||
<template #prepend>
|
||||
<v-icon v-if="item.icon" size="20" :icon="item.icon" />
|
||||
@@ -93,9 +93,9 @@ v-else :link="!!subItem.path" :prepend-icon="subItem.icon || mdiMenuRight" :to="
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { AdminLayoutMenuItem } from './types'
|
||||
import { mdiCircleSmall, mdiMenuRight } from '@mdi/js'
|
||||
import { computed, watch } from 'vue'
|
||||
import type { AdminLayoutMenuItem } from './types'
|
||||
|
||||
interface Props {
|
||||
opened?: string[]
|
||||
|
||||
@@ -14,8 +14,8 @@ v-for="item in mobileCurrentItems" :key="item.path ?? item.title" class="mb-1" r
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { mdiArrowTopRight, mdiChevronRight } from '@mdi/js'
|
||||
import type { AdminLayoutMenuItem } from './types'
|
||||
import { mdiArrowTopRight, mdiChevronRight } from '@mdi/js'
|
||||
|
||||
withDefaults(defineProps<{
|
||||
mobileCurrentItems?: AdminLayoutMenuItem[]
|
||||
|
||||
Reference in New Issue
Block a user