feat: 重構主佈局及相關元件,更新命名規則並新增功能
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
<template>
|
||||
<v-col class="d-flex align-center bg-surface pr-2 pl-2 pl-m-3 py-1">
|
||||
<v-btn
|
||||
v-if="isMobile"
|
||||
:icon="mdiMenu"
|
||||
size="small"
|
||||
variant="text"
|
||||
@click="emit('toggle-drawer')"
|
||||
></v-btn>
|
||||
|
||||
<div v-if="features.showSearch" class="search-input-wrapper">
|
||||
<span id="admin-layout-search-label" class="sr-only">{{ searchConfig.label }}</span>
|
||||
<v-text-field
|
||||
v-model="searchValueModel"
|
||||
aria-labelledby="admin-layout-search-label"
|
||||
:aria-label="searchConfig.label"
|
||||
class="search-input"
|
||||
density="compact"
|
||||
hide-details
|
||||
name="layout-search"
|
||||
:placeholder="searchConfig.placeholder"
|
||||
variant="outlined"
|
||||
@keyup.enter="triggerSearch"
|
||||
>
|
||||
<template v-if="!isMobile" #prepend-inner>
|
||||
<v-icon size="small" :icon="mdiMagnify" />
|
||||
</template>
|
||||
<template #append-inner>
|
||||
<v-btn
|
||||
:aria-label="searchConfig.label"
|
||||
color="primary"
|
||||
size="small"
|
||||
variant="text"
|
||||
@click="triggerSearch"
|
||||
>
|
||||
開始搜尋
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</div>
|
||||
|
||||
<div v-if="features.showToolbarActions" class="top-actions">
|
||||
<slot name="actions">
|
||||
<!-- 通知 -->
|
||||
<v-tooltip location="bottom" :text="toolbarActions.notificationsLabel">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-btn
|
||||
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"
|
||||
offset-x="4"
|
||||
offset-y="-2"
|
||||
>
|
||||
<v-icon :icon="mdiBellOutline" />
|
||||
</v-badge>
|
||||
<v-icon v-else :icon="mdiBellOutline" />
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
|
||||
<!-- 訊息 -->
|
||||
<v-tooltip location="bottom" :text="toolbarActions.messagesLabel">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-btn
|
||||
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"
|
||||
offset-y="-2"
|
||||
>
|
||||
<v-icon :icon="mdiMessageTextOutline" />
|
||||
</v-badge>
|
||||
<v-icon v-else :icon="mdiMessageTextOutline" />
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
|
||||
<!-- 說明 -->
|
||||
<v-tooltip v-if="false" location="bottom" :text="toolbarActions.helpLabel">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-btn
|
||||
v-bind="activatorProps"
|
||||
:aria-label="toolbarActions.helpLabel"
|
||||
icon
|
||||
size="small"
|
||||
variant="text"
|
||||
@click="emit('action', 'help')"
|
||||
>
|
||||
<v-icon :icon="mdiHelp" />
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
|
||||
<!-- 設定 -->
|
||||
<v-menu :close-on-content-click="false" location="bottom end">
|
||||
<template #activator="{ props: menuProps }">
|
||||
<v-tooltip location="bottom" :text="toolbarActions.settingsLabel">
|
||||
<template #activator="{ props: tooltipProps }">
|
||||
<v-btn
|
||||
v-bind="{ ...menuProps, ...tooltipProps }"
|
||||
:aria-label="toolbarActions.settingsLabel"
|
||||
icon
|
||||
size="small"
|
||||
variant="text"
|
||||
>
|
||||
<v-icon :icon="mdiCogOutline" />
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
<v-list density="compact" width="180">
|
||||
<v-list-subheader class="text-subtitle-1 py-2">顯示設定</v-list-subheader>
|
||||
<v-list-item>
|
||||
<v-switch
|
||||
v-model="showFavoritesBarModel"
|
||||
color="primary"
|
||||
density="comfortable"
|
||||
hide-details
|
||||
>
|
||||
<template #label>
|
||||
<span class="text-body-2" style="width: 8ch">常用功能</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch
|
||||
v-model="breadcrumbBarVisibleModel"
|
||||
color="primary"
|
||||
density="comfortable"
|
||||
hide-details
|
||||
>
|
||||
<template #label>
|
||||
<span class="text-body-2" style="width: 8ch">路徑</span>
|
||||
</template>
|
||||
</v-switch>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<!-- 登出 -->
|
||||
<v-tooltip location="bottom" :text="logoutLabel">
|
||||
<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: activatorProps }">
|
||||
<v-btn
|
||||
v-bind="activatorProps"
|
||||
:aria-label="themeToggleLabel"
|
||||
icon
|
||||
variant="text"
|
||||
@click="emit('toggle-theme')"
|
||||
>
|
||||
<v-icon :icon="mdiPaletteOutline" />
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</slot>
|
||||
</div>
|
||||
</v-col>
|
||||
</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'
|
||||
|
||||
interface Props {
|
||||
isMobile?: boolean
|
||||
features?: AdminLayoutFeatures
|
||||
searchValue?: string
|
||||
searchConfig?: AdminLayoutSearchConfig
|
||||
toolbarActions?: AdminLayoutToolbarActions
|
||||
toolbarCounts?: AdminLayoutToolbarCounts
|
||||
logoutLabel?: string
|
||||
themeToggleLabel?: string
|
||||
showFavoritesBar?: boolean
|
||||
breadcrumbBarVisible?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isMobile: false,
|
||||
features: () => ({
|
||||
showThemeToggle: false,
|
||||
showFavorites: true,
|
||||
showBreadcrumb: true,
|
||||
showSearch: true,
|
||||
showToolbarActions: true,
|
||||
showUserInfo: true,
|
||||
}),
|
||||
searchValue: '',
|
||||
searchConfig: () => ({
|
||||
placeholder: '',
|
||||
label: '',
|
||||
}),
|
||||
toolbarActions: () => ({
|
||||
notificationsLabel: '',
|
||||
messagesLabel: '',
|
||||
helpLabel: '',
|
||||
settingsLabel: '',
|
||||
}),
|
||||
toolbarCounts: () => ({
|
||||
notifications: 0,
|
||||
messages: 0,
|
||||
}),
|
||||
logoutLabel: '',
|
||||
themeToggleLabel: '',
|
||||
showFavoritesBar: true,
|
||||
breadcrumbBarVisible: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
'toggle-drawer': []
|
||||
'update:searchValue': [value: string]
|
||||
search: []
|
||||
action: [type: AdminLayoutActionType]
|
||||
logout: []
|
||||
'toggle-theme': []
|
||||
'update:show-favorites-bar': [value: boolean]
|
||||
'update:breadcrumb-bar-visible': [value: boolean]
|
||||
}>()
|
||||
|
||||
const searchValueModel = computed({
|
||||
get: () => props.searchValue,
|
||||
set: (value) => emit('update:searchValue', value),
|
||||
})
|
||||
|
||||
const showFavoritesBarModel = computed({
|
||||
get: () => props.showFavoritesBar,
|
||||
set: (value) => emit('update:show-favorites-bar', value),
|
||||
})
|
||||
|
||||
const breadcrumbBarVisibleModel = computed({
|
||||
get: () => props.breadcrumbBarVisible,
|
||||
set: (value) => emit('update:breadcrumb-bar-visible', value),
|
||||
})
|
||||
|
||||
function triggerSearch() {
|
||||
emit('search')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-input-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
:deep(.search-input-wrapper .v-field--appended) {
|
||||
padding-inline-end: 4px;
|
||||
}
|
||||
|
||||
.top-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user