mirror of
https://github.com/cuigh/swirl
synced 2025-06-26 18:16:50 +00:00
243 lines
6.5 KiB
Vue
243 lines
6.5 KiB
Vue
<template>
|
|
<n-layout :position="isMobile ? 'static' : 'absolute'">
|
|
<n-layout-header bordered>
|
|
<div class="header-left" align="center">
|
|
<n-popover
|
|
v-if="isMobile || isTablet"
|
|
style="padding: 0; width: 200px"
|
|
placement="bottom-end"
|
|
display-directive="show"
|
|
trigger="click"
|
|
ref="menuPopover"
|
|
>
|
|
<template #trigger>
|
|
<n-button size="small" style="margin-right: 8px">
|
|
<template #icon>
|
|
<n-icon>
|
|
<menu-outline />
|
|
</n-icon>
|
|
</template>
|
|
</n-button>
|
|
</template>
|
|
<div style="overflow: auto; max-height: 79vh">
|
|
<n-menu
|
|
:value="menuValue"
|
|
:options="menuOptions"
|
|
:indent="18"
|
|
@update:value="menuPopover.setShow(false)"
|
|
:render-label="renderMenuLabel"
|
|
/>
|
|
</div>
|
|
</n-popover>
|
|
<n-text tag="div" class="logo" :depth="1" @click="$router.push('/')">
|
|
<img src="/favicon.ico" v-if="!isMobile" />
|
|
Swirl
|
|
</n-text>
|
|
</div>
|
|
<n-space justify="end" align="center" class="header-right" :size="0">
|
|
<div style="margin-right: 10px; line-height: 56px">
|
|
<n-text depth="3">v{{ version.version }}</n-text>
|
|
</div>
|
|
<n-tooltip trigger="hover">
|
|
<template #trigger>
|
|
<n-button
|
|
type="default"
|
|
size="small"
|
|
:bordered="false"
|
|
tag="a"
|
|
href="https://github.com/cuigh/swirl"
|
|
target="_blank"
|
|
>
|
|
<template #icon>
|
|
<n-icon>
|
|
<LogoGithub />
|
|
</n-icon>
|
|
</template>
|
|
</n-button>
|
|
</template>
|
|
GitHub
|
|
</n-tooltip>
|
|
<n-dropdown @select="selectOption" trigger="hover" :options="dropdownOptions" show-arrow>
|
|
<n-button quaternary size="small">
|
|
<template #icon>
|
|
<n-icon>
|
|
<PersonOutline />
|
|
</n-icon>
|
|
</template>
|
|
{{ store.state.user?.name }}
|
|
</n-button>
|
|
</n-dropdown>
|
|
<n-tooltip trigger="hover">
|
|
<template #trigger>
|
|
<n-button size="small" quaternary @click="logout">
|
|
<template #icon>
|
|
<n-icon>
|
|
<LogOutOutline />
|
|
</n-icon>
|
|
</template>
|
|
</n-button>
|
|
</template>
|
|
{{ t('buttons.sign_out') }}
|
|
</n-tooltip>
|
|
</n-space>
|
|
</n-layout-header>
|
|
<n-layout
|
|
has-sider
|
|
:position="isMobile ? 'static' : 'absolute'"
|
|
:style="isMobile ? '' : 'top: 56px; bottom: 64px'"
|
|
>
|
|
<n-layout-sider
|
|
v-if="!isMobile && !isTablet"
|
|
bordered
|
|
width="200"
|
|
:collapsed-width="64"
|
|
:collapsed="collapsed"
|
|
collapse-mode="width"
|
|
show-trigger="bar"
|
|
trigger-style="right: -25px"
|
|
collapsed-trigger-style="right: -25px"
|
|
@collapse="collapsed = true"
|
|
@expand="collapsed = false"
|
|
>
|
|
<n-menu
|
|
:value="menuValue"
|
|
:options="menuOptions"
|
|
:collapsed="collapsed"
|
|
:collapsed-width="64"
|
|
:collapsed-icon-size="22"
|
|
:root-indent="20"
|
|
:indent="24"
|
|
:render-label="renderMenuLabel"
|
|
:expanded-keys="expandedKeys"
|
|
@update:expanded-keys="updateExpandedKeys"
|
|
/>
|
|
</n-layout-sider>
|
|
<n-layout-content>
|
|
<router-view></router-view>
|
|
<n-back-top :right="16" :bottom="10" />
|
|
</n-layout-content>
|
|
</n-layout>
|
|
<n-layout-footer bordered :position="isMobile ? 'static' : 'absolute'">
|
|
<span>{{ t('copyright') }}</span>
|
|
</n-layout-footer>
|
|
</n-layout>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, reactive, watch, onMounted } from "vue";
|
|
import {
|
|
NButton,
|
|
NIcon,
|
|
NMenu,
|
|
NText,
|
|
NSpace,
|
|
NLayout,
|
|
NLayoutHeader,
|
|
NLayoutSider,
|
|
NLayoutContent,
|
|
NLayoutFooter,
|
|
NPopover,
|
|
NTooltip,
|
|
NDropdown,
|
|
NSwitch,
|
|
NBackTop,
|
|
} from "naive-ui";
|
|
import { MenuOutline, PersonOutline, LogOutOutline, LogoGithub } from "@vicons/ionicons5";
|
|
import { RouterView, useRouter, useRoute } from "vue-router";
|
|
import { useStore } from "vuex";
|
|
import { useIsMobile, useIsTablet } from "@/utils";
|
|
import { findMenuValue, renderMenuLabel, menuOptions, findActiveOptions } from "@/router/menu";
|
|
import systemApi from "@/api/system";
|
|
import type { Version } from "@/api/system";
|
|
import { Mutations } from "@/store/mutations";
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
const { t } = useI18n()
|
|
const dropdownOptions = [
|
|
{
|
|
label: t('titles.profile'),
|
|
key: "profile",
|
|
},
|
|
];
|
|
const store = useStore();
|
|
const router = useRouter();
|
|
const route = useRoute();
|
|
const menuPopover = ref();
|
|
const collapsed = ref(false)
|
|
const expandedKeys = ref([] as string[]);
|
|
const isMobile = useIsMobile()
|
|
const isTablet = useIsTablet()
|
|
const darkTheme = computed(() => store.state.preference.theme === "dark")
|
|
const menuValue = computed(() => findMenuValue(route))
|
|
const version = ref({} as Version);
|
|
|
|
function updateExpandedKeys(data: any) {
|
|
expandedKeys.value = data
|
|
}
|
|
|
|
function selectOption(key: any) {
|
|
switch (key as string) {
|
|
case "profile":
|
|
router.push("/profile")
|
|
return
|
|
default:
|
|
console.info(key)
|
|
}
|
|
}
|
|
|
|
function logout() {
|
|
store.commit(Mutations.Logout);
|
|
router.push("/login");
|
|
}
|
|
|
|
watch(() => route.path, (path: string) => {
|
|
let keys = findActiveOptions(route).map((opt: any) => opt.key) as string[]
|
|
expandedKeys.value = keys;
|
|
})
|
|
|
|
onMounted(async () => {
|
|
const r = await systemApi.version();
|
|
version.value = r.data as Version;
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
::v-deep(.header-right .n-button__content) {
|
|
margin-top: 4px;
|
|
}
|
|
.header-left {
|
|
flex-grow: 1;
|
|
width: 180px;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.header-right {
|
|
width: 320px;
|
|
}
|
|
/* .n-layout-header {
|
|
background-color: #363636;
|
|
} */
|
|
.n-layout-sider {
|
|
box-shadow: 2px 0 4px -2px rgb(10 10 10 / 10%);
|
|
}
|
|
.n-layout-footer {
|
|
box-shadow: 0px -2px 4px -2px rgb(10 10 10 / 10%);
|
|
/* background-image: radial-gradient(circle at 1% 1%,#328bf2,#1644ad); */
|
|
}
|
|
/* .n-layout-header {
|
|
background-image: linear-gradient(to right, rgb(91, 121, 162) 0%, rgb(46, 68, 105) 100%);
|
|
}
|
|
.logo {
|
|
color: white;
|
|
}
|
|
.n-layout-header .n-icon {
|
|
color: white;
|
|
}
|
|
::v-deep(.n-layout-header .n-button__content) {
|
|
color: white;
|
|
}
|
|
::v-deep(.n-layout-header .n-button__content:hover) {
|
|
color: green;
|
|
} */
|
|
</style>
|