Files
skt-vuetify-templates/src/components/layouts/sk-admin-layout/SkAdminAppBarTopCol.vue
T

218 lines
7.1 KiB
Vue

<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">
<v-text-field
v-model="searchValueModel" :aria-label="searchConfig.label" class="search-input" density="compact"
hide-details :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 }">
<v-btn
v-bind="props" :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 }">
<v-btn
v-bind="props" :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 }">
<v-btn
v-bind="props" :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="showBreadcrumbBarModel" 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 }">
<v-btn v-bind="props" :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')">
<v-icon :icon="mdiPaletteOutline" />
</v-btn>
</template>
</v-tooltip>
</slot>
</div>
</v-col>
</template>
<script setup lang="ts">
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
features?: AdminLayoutFeatures
searchValue?: string
searchConfig?: AdminLayoutSearchConfig
toolbarActions?: AdminLayoutToolbarActions
toolbarCounts?: AdminLayoutToolbarCounts
logoutLabel?: string
themeToggleLabel?: string
showFavoritesBar?: boolean
showBreadcrumbBar?: 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,
showBreadcrumbBar: true,
})
const emit = defineEmits<{
'toggle-drawer': []
'update:searchValue': [value: string]
search: []
action: [type: AdminLayoutActionType]
logout: []
'toggle-theme': []
'update:showFavoritesBar': [value: boolean]
'update:showBreadcrumbBar': [value: boolean]
}>()
const searchValueModel = computed({
get: () => props.searchValue,
set: (value) => emit('update:searchValue', value),
})
const showFavoritesBarModel = computed({
get: () => props.showFavoritesBar,
set: (value) => emit('update:showFavoritesBar', value),
})
const showBreadcrumbBarModel = computed({
get: () => props.showBreadcrumbBar,
set: (value) => emit('update:showBreadcrumbBar', 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;
}
</style>