feat: 平板模式

This commit is contained in:
alger
2025-10-11 20:24:11 +08:00
parent cb2baeadf5
commit 29ba231a7d
10 changed files with 99 additions and 49 deletions

View File

@@ -58,7 +58,9 @@ export default {
'Changing GPU acceleration settings requires application restart to take effect',
gpuAccelerationChangeSuccess:
'GPU acceleration settings updated, restart application to take effect',
gpuAccelerationChangeError: 'Failed to update GPU acceleration settings'
gpuAccelerationChangeError: 'Failed to update GPU acceleration settings',
tabletMode: 'Tablet Mode',
tabletModeDesc: 'Enabling tablet mode allows using PC-style interface on mobile devices'
},
playback: {
quality: 'Audio Quality',

View File

@@ -57,7 +57,10 @@ export default {
gpuAccelerationRestart: 'GPUアクセラレーション設定の変更はアプリの再起動後に有効になります',
gpuAccelerationChangeSuccess:
'GPUアクセラレーション設定を更新しました。アプリの再起動後に有効になります',
gpuAccelerationChangeError: 'GPUアクセラレーション設定の更新に失敗しました'
gpuAccelerationChangeError: 'GPUアクセラレーション設定の更新に失敗しました',
tabletMode: 'タブレットモード',
tabletModeDesc:
'タブレットモードを有効にすると、モバイルデバイスでPCスタイルのインターフェースを使用できます'
},
playback: {
quality: '音質設定',

View File

@@ -57,7 +57,10 @@ export default {
gpuAccelerationRestart: 'GPU 가속 설정을 변경하면 애플리케이션을 다시 시작해야 합니다',
gpuAccelerationChangeSuccess:
'GPU 가속 설정이 업데이트되었습니다. 애플리케이션을 다시 시작하여 적용하십시오',
gpuAccelerationChangeError: 'GPU 가속 설정 업데이트에 실패했습니다'
gpuAccelerationChangeError: 'GPU 가속 설정 업데이트에 실패했습니다',
tabletMode: '태블릿 모드',
tabletModeDesc:
'태블릿 모드를 사용하면 모바일 기기에서 PC 스타일의 인터페이스를 사용할 수 있습니다'
},
playback: {
quality: '음질 설정',

View File

@@ -55,7 +55,9 @@ export default {
gpuAccelerationDesc: '启用或禁用硬件加速可以提高渲染性能但可能会增加GPU负载',
gpuAccelerationRestart: '更改GPU加速设置需要重启应用后生效',
gpuAccelerationChangeSuccess: 'GPU加速设置已更新重启应用后生效',
gpuAccelerationChangeError: 'GPU加速设置更新失败'
gpuAccelerationChangeError: 'GPU加速设置更新失败',
tabletMode: '平板模式',
tabletModeDesc: '启用后将在移动设备上使用PC样式界面适合平板等大屏设备'
},
playback: {
quality: '音质设置',

View File

@@ -50,7 +50,14 @@ export default {
englishText: 'The quick brown fox jumps over the lazy dog',
japaneseText: 'あいうえお かきくけこ さしすせそ',
koreanText: '가나다라마 바사아자차 카타파하'
}
},
gpuAcceleration: 'GPU加速',
gpuAccelerationDesc: '啟用或禁用硬體加速可以提高渲染性能但可能會增加GPU負載',
gpuAccelerationRestart: '更改GPU加速設定需要重啟應用後生效',
gpuAccelerationChangeSuccess: 'GPU加速設定已更新重啟應用後生效',
gpuAccelerationChangeError: 'GPU加速設定更新失敗',
tabletMode: '平板模式',
tabletModeDesc: '啟用後將在移動設備上使用PC樣式界面適合平板等大屏設備'
},
playback: {
quality: '音質設定',

View File

@@ -19,8 +19,6 @@ import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import TrafficWarningDrawer from '@/components/TrafficWarningDrawer.vue';
import homeRouter from '@/router/home';
import { useMenuStore } from '@/store/modules/menu';
import { usePlayerStore } from '@/store/modules/player';
import { useSettingsStore } from '@/store/modules/settings';
import { isElectron, isLyricWindow } from '@/utils';
@@ -32,7 +30,6 @@ import { useAppShortcuts } from './utils/appShortcuts';
const { locale } = useI18n();
const settingsStore = useSettingsStore();
const menuStore = useMenuStore();
const playerStore = usePlayerStore();
const router = useRouter();
@@ -75,9 +72,6 @@ if (!isLyricWindow.value) {
settingsStore.initializeSettings();
settingsStore.initializeTheme();
settingsStore.initializeSystemFonts();
if (isMobile.value) {
menuStore.setMenus(homeRouter.filter((item) => item.meta.isMobile));
}
}
handleSetLanguage(settingsStore.setData.language);

View File

@@ -4,7 +4,7 @@
<title-bar />
<div class="layout-main-page">
<!-- 侧边菜单栏 -->
<app-menu v-if="!isMobile" class="menu" :menus="menus" />
<app-menu v-if="!settingsStore.isMobile" class="menu" :menus="menuStore.menus" />
<div class="main">
<!-- 搜索栏 -->
<search-bar />
@@ -17,7 +17,7 @@
<router-view
v-slot="{ Component }"
class="main-page"
:class="route.meta.noScroll && !isMobile ? 'pr-3' : ''"
:class="route.meta.noScroll && !settingsStore.isMobile ? 'pr-3' : ''"
>
<keep-alive :include="keepAliveInclude">
<component :is="Component" />
@@ -25,27 +25,27 @@
</router-view>
</div>
<play-bottom />
<app-menu v-if="shouldShowMobileMenu" class="menu" :menus="menus" />
<app-menu v-if="shouldShowMobileMenu" class="menu" :menus="menuStore.menus" />
</div>
</div>
<!-- 底部音乐播放 -->
<template v-if="!settingsStore.isMiniMode">
<play-bar
v-if="!isMobile"
v-if="!settingsStore.isMobile"
v-show="isPlay"
:style="playerStore.musicFull ? 'bottom: 0;' : ''"
/>
<mobile-play-bar
v-else
v-show="isPlay"
:style="isMobile && playerStore.musicFull ? 'bottom: 0;' : ''"
:style="settingsStore.isMobile && playerStore.musicFull ? 'bottom: 0;' : ''"
/>
</template>
</div>
<install-app-modal v-if="!isElectron"></install-app-modal>
<update-modal v-if="isElectron" />
<playlist-drawer v-model="showPlaylistDrawer" :song-id="currentSongId" />
<sleep-timer-top v-if="!isMobile" />
<sleep-timer-top v-if="!settingsStore.isMobile" />
<!-- 下载管理抽屉 -->
<download-drawer
v-if="
@@ -74,7 +74,7 @@ import otherRouter from '@/router/other';
import { useMenuStore } from '@/store/modules/menu';
import { usePlayerStore } from '@/store/modules/player';
import { useSettingsStore } from '@/store/modules/settings';
import { isElectron, isMobile } from '@/utils';
import { isElectron } from '@/utils';
const keepAliveInclude = computed(() => {
const allRoutes = [...homeRouter, ...otherRouter];
@@ -106,15 +106,14 @@ const settingsStore = useSettingsStore();
const menuStore = useMenuStore();
const isPlay = computed(() => playerStore.playMusic && playerStore.playMusic.id);
const { menus } = menuStore;
const route = useRoute();
// 判断当前路由是否应该在移动端显示AppMenu
const shouldShowMobileMenu = computed(() => {
// 过滤出在menus中定义的路径
const menuPaths = menus.map((item: any) => item.path);
const menuPaths = menuStore.menus.map((item: any) => item.path);
// 检查当前路由路径是否在menus中
return menuPaths.includes(route.path) && isMobile.value && !playerStore.musicFull;
return menuPaths.includes(route.path) && settingsStore.isMobile && !playerStore.musicFull;
});
provide('shouldShowMobileMenu', shouldShowMobileMenu);

View File

@@ -1,8 +1,10 @@
import { cloneDeep, merge } from 'lodash';
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { ref, watch } from 'vue';
import setDataDefault from '@/../main/set.json';
import homeRouter from '@/router/home';
import { useMenuStore } from '@/store/modules/menu';
import { isElectron } from '@/utils';
import {
applyTheme,
@@ -197,6 +199,56 @@ export const useSettingsStore = defineStore('settings', () => {
}
};
// 计算移动端状态的函数
const calculateMobileStatus = () => {
const userAgentFlag = navigator.userAgent.match(
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
);
const isMobileWidth = window.innerWidth < 500;
const isMobileDevice = !!userAgentFlag || isMobileWidth;
const tabletMode = setData.value?.tabletMode;
return isMobileDevice && !tabletMode;
};
// 更新移动端状态和DOM类
const updateMobileStatus = () => {
const menuStore = useMenuStore();
const shouldUseMobileStyle = calculateMobileStatus();
// 更新store状态
if (shouldUseMobileStyle) {
menuStore.setMenus(homeRouter.filter((item) => item.meta.isMobile));
} else {
menuStore.setMenus(homeRouter);
}
// 更新DOM类
if (shouldUseMobileStyle) {
document.documentElement.classList.add('mobile');
document.documentElement.classList.remove('pc');
} else {
document.documentElement.classList.add('pc');
document.documentElement.classList.remove('mobile');
}
isMobile.value = shouldUseMobileStyle;
};
// 监听平板模式变化
watch(
() => setData.value?.tabletMode,
() => {
updateMobileStatus();
},
{ immediate: true }
);
// 监听窗口大小变化
if (typeof window !== 'undefined') {
window.addEventListener('resize', updateMobileStatus);
}
return {
setData,
theme,

View File

@@ -1,4 +1,3 @@
import { useWindowSize } from '@vueuse/core';
import { computed } from 'vue';
import { useSettingsStore } from '@/store/modules/settings';
@@ -74,26 +73,8 @@ export const getImgUrl = (url: string | undefined, size: string = '') => {
};
export const isMobile = computed(() => {
const { width } = useWindowSize();
const userAgentFlag = navigator.userAgent.match(
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
);
const isMobileWidth = width.value < 500;
const isMobileDevice = !!userAgentFlag || isMobileWidth;
const settingsStore = useSettingsStore();
settingsStore.isMobile = isMobileDevice;
// 给html标签 添加或移除mobile类
if (isMobileDevice) {
document.documentElement.classList.add('mobile');
} else {
document.documentElement.classList.add('pc');
document.documentElement.classList.remove('mobile');
}
return isMobileDevice;
return settingsStore.isMobile;
});
export const isElectron = (window as any).electron !== undefined;

View File

@@ -59,6 +59,20 @@
<language-switcher />
</div>
<!-- 平板模式设置 -->
<div class="set-item" v-if="!isElectron">
<div>
<div class="set-item-title">{{ t('settings.basic.tabletMode') }}</div>
<div class="set-item-content">
{{ t('settings.basic.tabletModeDesc') }}
</div>
</div>
<n-switch v-model:value="setData.tabletMode">
<template #checked><i class="ri-tablet-line"></i></template>
<template #unchecked><i class="ri-smartphone-line"></i></template>
</n-switch>
</div>
<div class="set-item">
<div>
<div class="set-item-title">{{ t('settings.translationEngine') }}</div>
@@ -642,7 +656,6 @@ const localSetData = ref({ ...settingsStore.setData });
// 在组件卸载时保存设置
onUnmounted(() => {
// 确保最终设置被保存
settingsStore.setSetData(localSetData.value);
});
@@ -656,16 +669,10 @@ const updateInfo = ref<UpdateResult>({
const { t } = useI18n();
// 创建一个防抖的保存函数
// const debouncedSaveSettings = debounce((newData) => {
// settingsStore.setSetData(newData);
// }, 500);
const saveSettings = useDebounceFn((data) => {
settingsStore.setSetData(data);
}, 500);
// 使用计算属性来管理设置数据
const setData = computed({
get: () => localSetData.value,
set: (newData) => {