refactor(App): 移除窗口控制按钮并重构标题栏结构
- 移除 src-tauri/capabilities/default.json 中的 core🪟allow-toggle-maximize 权限
- 从 App.vue 中移除自定义标题栏实现,包括拖拽区域和窗口控制按钮
- 移除与窗口控制相关的函数(minimizeWindow、toggleMaximizeWindow、closeWindow 等)
- 移除标题栏相关样式和 CSS 类
- 新增独立的 WindowTitlebar 组件来处理窗口控制功能
- 在 MainLayout.vue 中集成新的 WindowTitlebar 组件
- 调整布局结构以支持新的标题栏组件
- 更新全局样式变量,添加 --playerbar-height 变量
- 移除 DiscoverView.vue 中未使用的分类头部元素
- 在 SettingsView.vue 中导入 computed 函数
此重构简化了主应用组件,并将窗口控制功能分离到专用组件中,
提高了代码的模块化程度和可维护性。
This commit is contained in:
parent
5bed7cd4cb
commit
cc2fdfa04f
|
|
@ -9,7 +9,6 @@
|
||||||
"core:window:allow-start-dragging",
|
"core:window:allow-start-dragging",
|
||||||
"core:window:allow-close",
|
"core:window:allow-close",
|
||||||
"core:window:allow-minimize",
|
"core:window:allow-minimize",
|
||||||
"core:window:allow-toggle-maximize",
|
|
||||||
"opener:default",
|
"opener:default",
|
||||||
"store:default"
|
"store:default"
|
||||||
]
|
]
|
||||||
|
|
|
||||||
205
src/App.vue
205
src/App.vue
|
|
@ -1,36 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="app-root">
|
<n-config-provider :theme-overrides="themeOverrides">
|
||||||
<div class="titlebar" :class="{ 'titlebar-macos': isMacos }">
|
<n-message-provider>
|
||||||
<div class="titlebar-drag" data-tauri-drag-region @mousedown="handleTitlebarMouseDown">
|
<n-notification-provider>
|
||||||
<span class="titlebar-dot"></span>
|
<n-dialog-provider>
|
||||||
<span class="titlebar-title">YwYMusic</span>
|
<div class="app-root">
|
||||||
</div>
|
<RouterView />
|
||||||
|
</div>
|
||||||
<div class="titlebar-controls">
|
</n-dialog-provider>
|
||||||
<button type="button" aria-label="最小化" @click="minimizeWindow">
|
</n-notification-provider>
|
||||||
<span class="window-icon window-icon-minimize"></span>
|
</n-message-provider>
|
||||||
</button>
|
</n-config-provider>
|
||||||
<button type="button" aria-label="最大化或还原" @click="toggleMaximizeWindow">
|
|
||||||
<span class="window-icon window-icon-maximize"></span>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="titlebar-close" aria-label="关闭" @click="closeWindow">
|
|
||||||
<span class="window-icon window-icon-close"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<n-config-provider :theme-overrides="themeOverrides">
|
|
||||||
<n-message-provider>
|
|
||||||
<n-notification-provider>
|
|
||||||
<n-dialog-provider>
|
|
||||||
<div class="app-content">
|
|
||||||
<RouterView />
|
|
||||||
</div>
|
|
||||||
</n-dialog-provider>
|
|
||||||
</n-notification-provider>
|
|
||||||
</n-message-provider>
|
|
||||||
</n-config-provider>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
@ -41,12 +20,8 @@ import {
|
||||||
NNotificationProvider,
|
NNotificationProvider,
|
||||||
type GlobalThemeOverrides,
|
type GlobalThemeOverrides,
|
||||||
} from "naive-ui";
|
} from "naive-ui";
|
||||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
|
||||||
import { RouterView } from "vue-router";
|
import { RouterView } from "vue-router";
|
||||||
|
|
||||||
const appWindow = getCurrentWindow();
|
|
||||||
const isMacos = navigator.userAgent.includes("Mac OS X");
|
|
||||||
|
|
||||||
const themeOverrides: GlobalThemeOverrides = {
|
const themeOverrides: GlobalThemeOverrides = {
|
||||||
common: {
|
common: {
|
||||||
primaryColor: "#b4532a",
|
primaryColor: "#b4532a",
|
||||||
|
|
@ -75,168 +50,12 @@ const themeOverrides: GlobalThemeOverrides = {
|
||||||
"'Inter','HarmonyOS Sans SC','PingFang SC','Microsoft YaHei',system-ui,-apple-system,sans-serif",
|
"'Inter','HarmonyOS Sans SC','PingFang SC','Microsoft YaHei',system-ui,-apple-system,sans-serif",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function minimizeWindow() {
|
|
||||||
void appWindow.minimize();
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleMaximizeWindow() {
|
|
||||||
void appWindow.toggleMaximize();
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeWindow() {
|
|
||||||
void appWindow.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTitlebarMouseDown(event: MouseEvent) {
|
|
||||||
if (event.detail === 2) {
|
|
||||||
void appWindow.toggleMaximize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.app-root {
|
.app-root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-top: var(--titlebar-height);
|
min-height: 0;
|
||||||
background: var(--app-bg);
|
background: var(--app-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.titlebar {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 9999;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr auto;
|
|
||||||
height: var(--titlebar-height);
|
|
||||||
overflow: hidden;
|
|
||||||
user-select: none;
|
|
||||||
background: rgba(234, 220, 200, 0.86);
|
|
||||||
border-bottom: 1px solid rgba(180, 83, 42, 0.12);
|
|
||||||
backdrop-filter: blur(16px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.titlebar-drag {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
gap: 8px;
|
|
||||||
padding-left: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.titlebar-macos {
|
|
||||||
background: rgba(244, 237, 223, 0.72);
|
|
||||||
}
|
|
||||||
|
|
||||||
.titlebar-macos .titlebar-drag {
|
|
||||||
padding-left: 78px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.titlebar-macos .titlebar-controls,
|
|
||||||
.titlebar-macos .titlebar-dot {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.titlebar-macos .titlebar-title {
|
|
||||||
color: var(--app-text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.titlebar-dot {
|
|
||||||
width: 9px;
|
|
||||||
height: 9px;
|
|
||||||
border-radius: 999px;
|
|
||||||
background: linear-gradient(135deg, #d98b5f 0%, var(--app-primary) 100%);
|
|
||||||
box-shadow: 0 0 0 3px rgba(180, 83, 42, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
.titlebar-title {
|
|
||||||
color: var(--app-text-secondary);
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: 0.2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.titlebar-controls {
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.titlebar-controls button {
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
width: 46px;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0;
|
|
||||||
color: var(--app-text-secondary);
|
|
||||||
background: transparent;
|
|
||||||
border: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
transition:
|
|
||||||
color 0.15s ease,
|
|
||||||
background-color 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.titlebar-controls button:hover {
|
|
||||||
color: var(--app-text);
|
|
||||||
background: rgba(180, 83, 42, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.titlebar-controls .titlebar-close:hover {
|
|
||||||
color: #fffaf0;
|
|
||||||
background: #d83b32;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-icon {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-icon-minimize::before,
|
|
||||||
.window-icon-maximize::before,
|
|
||||||
.window-icon-close::before,
|
|
||||||
.window-icon-close::after {
|
|
||||||
position: absolute;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-icon-minimize::before {
|
|
||||||
right: 1px;
|
|
||||||
bottom: 3px;
|
|
||||||
left: 1px;
|
|
||||||
height: 1.5px;
|
|
||||||
background: currentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-icon-maximize::before {
|
|
||||||
inset: 1px;
|
|
||||||
border: 1.5px solid currentColor;
|
|
||||||
border-radius: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-icon-close::before,
|
|
||||||
.window-icon-close::after {
|
|
||||||
top: 5px;
|
|
||||||
left: 0;
|
|
||||||
width: 12px;
|
|
||||||
height: 1.5px;
|
|
||||||
background: currentColor;
|
|
||||||
border-radius: 999px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-icon-close::before {
|
|
||||||
transform: rotate(45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-icon-close::after {
|
|
||||||
transform: rotate(-45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-content {
|
|
||||||
height: calc(100vh - var(--titlebar-height));
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -310,7 +310,7 @@ onUnmounted(() => {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: clamp(12px, 2vw, 20px);
|
gap: clamp(12px, 2vw, 20px);
|
||||||
height: clamp(72px, 9vh, 88px);
|
height: 100%;
|
||||||
padding: 0 clamp(14px, 2vw, 22px);
|
padding: 0 clamp(14px, 2vw, 22px);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: rgba(255, 250, 240, 0.94);
|
background: rgba(255, 250, 240, 0.94);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
<template>
|
||||||
|
<header class="window-titlebar" :class="{ 'window-titlebar--macos': isMacos }">
|
||||||
|
<div class="window-titlebar__drag" data-tauri-drag-region>
|
||||||
|
<!-- <span class="window-titlebar__dot"></span> -->
|
||||||
|
<span class="window-titlebar__title">YwYMusic</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="window-titlebar__controls">
|
||||||
|
<n-button
|
||||||
|
class="window-titlebar__control"
|
||||||
|
quaternary
|
||||||
|
:bordered="false"
|
||||||
|
aria-label="最小化"
|
||||||
|
@click.stop="minimizeWindow"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<n-icon :component="MinimizeOutlined" :size="18" />
|
||||||
|
</template>
|
||||||
|
</n-button>
|
||||||
|
|
||||||
|
<n-button
|
||||||
|
class="window-titlebar__control window-titlebar__control--close"
|
||||||
|
quaternary
|
||||||
|
:bordered="false"
|
||||||
|
aria-label="关闭"
|
||||||
|
@click.stop="closeWindow"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<n-icon :component="CloseOutlined" :size="18" />
|
||||||
|
</template>
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getCurrentWindow } from "@tauri-apps/api/window"
|
||||||
|
import { NButton, NIcon } from "naive-ui"
|
||||||
|
import { CloseOutlined, MinimizeOutlined } from "@vicons/material"
|
||||||
|
|
||||||
|
const appWindow = getCurrentWindow()
|
||||||
|
const isMacos = navigator.userAgent.includes("Mac OS X")
|
||||||
|
|
||||||
|
function minimizeWindow() {
|
||||||
|
void appWindow.minimize()
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeWindow() {
|
||||||
|
void appWindow.close()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.window-titlebar {
|
||||||
|
position: relative;
|
||||||
|
z-index: 4;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
height: var(--titlebar-height);
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
|
background: rgba(234, 220, 200, 0.86);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar__drag {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
|
gap: 8px;
|
||||||
|
padding-left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar__dot {
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: linear-gradient(135deg, #d98b5f 0%, var(--app-primary) 100%);
|
||||||
|
box-shadow: 0 0 0 3px rgba(180, 83, 42, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar__title {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--app-text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar__controls {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar__control {
|
||||||
|
width: 46px;
|
||||||
|
height: var(--titlebar-height);
|
||||||
|
--n-border-radius: 0 !important;
|
||||||
|
--n-color-hover: rgba(180, 83, 42, 0.1) !important;
|
||||||
|
--n-color-pressed: rgba(180, 83, 42, 0.16) !important;
|
||||||
|
--n-text-color: var(--app-text-secondary) !important;
|
||||||
|
--n-text-color-hover: var(--app-text) !important;
|
||||||
|
--n-text-color-pressed: var(--app-text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar__control--close {
|
||||||
|
--n-color-hover: #d83b32 !important;
|
||||||
|
--n-color-pressed: #c43129 !important;
|
||||||
|
--n-text-color-hover: #fffaf0 !important;
|
||||||
|
--n-text-color-pressed: #fffaf0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar--macos {
|
||||||
|
background: rgba(244, 237, 223, 0.72);
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar--macos .window-titlebar__drag {
|
||||||
|
padding-left: 78px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar--macos .window-titlebar__controls,
|
||||||
|
.window-titlebar--macos .window-titlebar__dot {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar--macos .window-titlebar__title {
|
||||||
|
color: var(--app-text-muted);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -38,11 +38,15 @@
|
||||||
</div>
|
</div>
|
||||||
</n-layout-sider>
|
</n-layout-sider>
|
||||||
|
|
||||||
<n-layout-content ref="contentRef" class="content-layout" :native-scrollbar="false">
|
<div class="content-pane">
|
||||||
<main class="content">
|
<WindowTitlebar />
|
||||||
<RouterView />
|
|
||||||
</main>
|
<n-layout-content ref="contentRef" class="content-layout" :native-scrollbar="false">
|
||||||
</n-layout-content>
|
<main class="content">
|
||||||
|
<RouterView />
|
||||||
|
</main>
|
||||||
|
</n-layout-content>
|
||||||
|
</div>
|
||||||
</n-layout>
|
</n-layout>
|
||||||
|
|
||||||
<n-layout-footer class="playerbar-footer">
|
<n-layout-footer class="playerbar-footer">
|
||||||
|
|
@ -61,6 +65,7 @@ import { NIcon } from "naive-ui"
|
||||||
import { MusicalNotesOutline, SearchOutline, SettingsOutline, TrophyOutline } from "@vicons/ionicons5"
|
import { MusicalNotesOutline, SearchOutline, SettingsOutline, TrophyOutline } from "@vicons/ionicons5"
|
||||||
import { Playlist } from "@vicons/tabler"
|
import { Playlist } from "@vicons/tabler"
|
||||||
import PlayerBar from "@/components/PlayerBar.vue"
|
import PlayerBar from "@/components/PlayerBar.vue"
|
||||||
|
import WindowTitlebar from "@/components/WindowTitlebar.vue"
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
@ -135,19 +140,35 @@ watch(
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.app-shell {
|
.app-shell {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: minmax(0, 1fr) auto;
|
grid-template-rows: minmax(0, 1fr) var(--playerbar-height);
|
||||||
height: 100%;
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
height: 100dvh;
|
||||||
|
max-height: 100vh;
|
||||||
|
max-height: 100dvh;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--app-bg);
|
background: var(--app-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace {
|
.workspace {
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-pane {
|
||||||
|
display: grid;
|
||||||
|
flex: 1;
|
||||||
|
grid-template-rows: var(--titlebar-height) minmax(0, 1fr);
|
||||||
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
background: var(--app-bg);
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-sider {
|
.sidebar-sider {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
@ -259,7 +280,9 @@ watch(
|
||||||
|
|
||||||
.content-layout {
|
.content-layout {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
|
@ -269,6 +292,9 @@ watch(
|
||||||
}
|
}
|
||||||
|
|
||||||
.playerbar-footer {
|
.playerbar-footer {
|
||||||
|
height: var(--playerbar-height);
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
:root {
|
:root {
|
||||||
--titlebar-height: 34px;
|
--titlebar-height: 34px;
|
||||||
|
--playerbar-height: clamp(72px, 9vh, 88px);
|
||||||
--app-bg: #f4eddf;
|
--app-bg: #f4eddf;
|
||||||
--app-bg-elevated: #eee2d1;
|
--app-bg-elevated: #eee2d1;
|
||||||
--app-card: #fffaf0;
|
--app-card: #fffaf0;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<section class="section">
|
<section>
|
||||||
<div class="section-head">
|
<!-- <div class="section-head">
|
||||||
<h3>分类</h3>
|
<h3>分类</h3>
|
||||||
<span class="section-sub">歌单分类</span>
|
<span class="section-sub">歌单分类</span>
|
||||||
</div>
|
</div> -->
|
||||||
<n-tabs v-model:value="activeCategory" type="line" animated @update:value="playList">
|
<n-tabs v-model:value="activeCategory" type="line" animated @update:value="playList">
|
||||||
<n-tab-pane v-for="category in categories" :key="category.id" :name="category.id" :tab="category.name" />
|
<n-tab-pane v-for="category in categories" :key="category.id" :name="category.id" :tab="category.name" />
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue"
|
||||||
import { RouterView, useRoute, useRouter } from "vue-router"
|
import { RouterView, useRoute, useRouter } from "vue-router"
|
||||||
import type { MenuOption } from "naive-ui"
|
import type { MenuOption } from "naive-ui"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue