feat(uiMode): 添加UI模式手动切换功能

This commit is contained in:
PKC278
2025-12-30 01:42:43 +08:00
parent 6b8ed8d527
commit b98512789f
6 changed files with 102 additions and 8 deletions
+14
View File
@@ -12,6 +12,16 @@ const globalPwaStatus = ref<{
const globalLoading = ref(false) const globalLoading = ref(false)
let initPromise: Promise<void> | null = null let initPromise: Promise<void> | null = null
// UI模式设置
export type UIMode = 'auto' | 'desktop' | 'app'
const uiMode = ref<UIMode>((localStorage.getItem('ui-mode') as UIMode) || 'auto')
// 设置UI模式
function setUIMode(mode: UIMode) {
uiMode.value = mode
localStorage.setItem('ui-mode', mode)
}
// 全局初始化函数 // 全局初始化函数
async function initializePWAGlobally() { async function initializePWAGlobally() {
if (initPromise) return initPromise if (initPromise) return initPromise
@@ -50,6 +60,8 @@ export function usePWA() {
}) })
const appMode = computed(() => { const appMode = computed(() => {
if (uiMode.value === 'app') return true
if (uiMode.value === 'desktop') return false
return pwaMode.value && display.mdAndDown.value return pwaMode.value && display.mdAndDown.value
}) })
@@ -70,6 +82,8 @@ export function usePWA() {
pwaMode, pwaMode,
appMode, appMode,
pwaStatus, pwaStatus,
uiMode,
setUIMode,
loading: globalLoading, loading: globalLoading,
initializePWA: initializePWAGlobally, initializePWA: initializePWAGlobally,
} }
+4 -5
View File
@@ -236,16 +236,15 @@ export function usePullDownGesture(options: PullDownOptions = {}) {
} }
} }
// PWA状态确定后,一次性决定是否添加事件监听器 // 监听 appMode 变化动态添加/移除事件监听器
onMounted(() => { onMounted(() => {
// 等待PWA检测完成后添加事件监听器 watch(
const stopWatcher = watch(
appMode, appMode,
newValue => { newValue => {
if (newValue) { if (newValue) {
addEventListeners() addEventListeners()
// PWA状态确定后停止监听 } else {
stopWatcher() removeEventListeners()
} }
}, },
{ immediate: true }, { immediate: true },
+78 -3
View File
@@ -16,6 +16,7 @@ import { saveLocalTheme } from '@/@core/utils/theme'
import type { ThemeSwitcherTheme } from '@layouts/types' import type { ThemeSwitcherTheme } from '@layouts/types'
import { useConfirm } from '@/composables/useConfirm' import { useConfirm } from '@/composables/useConfirm'
import { themeManager } from '@/utils/themeManager' import { themeManager } from '@/utils/themeManager'
import { usePWA, type UIMode } from '@/composables/usePWA'
// 认证 Store // 认证 Store
const authStore = useAuthStore() const authStore = useAuthStore()
@@ -27,6 +28,8 @@ const globalSettingsStore = useGlobalSettingsStore()
const { t } = useI18n() const { t } = useI18n()
// 显示器 // 显示器
const display = useDisplay() const display = useDisplay()
// PWA
const { uiMode, setUIMode } = usePWA()
// 提示框 // 提示框
const $toast = useToast() const $toast = useToast()
@@ -40,6 +43,9 @@ const siteAuthDialog = ref(false)
// 自定义CSS弹窗 // 自定义CSS弹窗
const cssDialog = ref(false) const cssDialog = ref(false)
// UI模式菜单是否显示
const showUIModeMenu = ref(false)
// 主题菜单是否显示 // 主题菜单是否显示
const showThemeMenu = ref(false) const showThemeMenu = ref(false)
@@ -233,6 +239,39 @@ const isAdvancedMode = computed(() => {
return globalSettingsStore.get('ADVANCED_MODE') !== false return globalSettingsStore.get('ADVANCED_MODE') !== false
}) })
// UI模式相关
const uiModes = computed(() => [
{
name: 'auto',
title: t('theme.autoUI'),
icon: 'mdi-devices',
},
{
name: 'desktop',
title: t('pwa.platforms.desktop'),
icon: 'mdi-monitor',
},
{
name: 'app',
title: t('pwa.platforms.mobile'),
icon: 'mdi-cellphone',
},
])
// 切换UI模式
function changeUIMode(mode: UIMode) {
setUIMode(mode)
showUIModeMenu.value = false
// 刷新页面以应用更改
window.location.reload()
}
// 获取当前UI模式图标
const getUIModeIcon = computed(() => {
const mode = uiModes.value.find(m => m.name === uiMode.value)
return mode?.icon || 'mdi-devices'
})
// 主题相关功能 // 主题相关功能
const { name: themeName, global: globalTheme } = useTheme() const { name: themeName, global: globalTheme } = useTheme()
const savedTheme = ref(localStorage.getItem('theme') ?? themeName) const savedTheme = ref(localStorage.getItem('theme') ?? themeName)
@@ -546,6 +585,41 @@ onUnmounted(() => {
<VListItemTitle>{{ t('user.siteAuth') }}</VListItemTitle> <VListItemTitle>{{ t('user.siteAuth') }}</VListItemTitle>
</VListItem> </VListItem>
<!-- 👉 UI模式设置 - 使用嵌套菜单 -->
<VMenu location="end" offset-x min-width="200" v-model="showUIModeMenu" :close-on-content-click="true">
<template v-slot:activator="{ props: menuProps }">
<VListItem v-bind="menuProps" class="mb-1 rounded-lg" hover>
<template #prepend>
<VIcon :icon="getUIModeIcon" />
</template>
<VListItemTitle>{{ t('common.uiMode') }}</VListItemTitle>
<VListItemSubtitle>
{{ uiModes.find(m => m.name === uiMode)?.title || t('theme.autoUI') }}
</VListItemSubtitle>
<template #append>
<VIcon icon="mdi-chevron-right" size="small" />
</template>
</VListItem>
</template>
<VList>
<VListItem
v-for="mode in uiModes"
:key="mode.name"
@click="changeUIMode(mode.name as UIMode)"
:active="uiMode === mode.name"
class="mb-1"
>
<template #prepend>
<VIcon :icon="mode.icon" />
</template>
<VListItemTitle>{{ mode.title }}</VListItemTitle>
<template #append v-if="uiMode === mode.name">
<VIcon icon="mdi-check" color="primary" size="small" />
</template>
</VListItem>
</VList>
</VMenu>
<!-- 👉 主题设置 - 使用嵌套菜单 --> <!-- 👉 主题设置 - 使用嵌套菜单 -->
<VMenu location="end" offset-x min-width="200" v-model="showThemeMenu" :close-on-content-click="true"> <VMenu location="end" offset-x min-width="200" v-model="showThemeMenu" :close-on-content-click="true">
<template v-slot:activator="{ props: menuProps }"> <template v-slot:activator="{ props: menuProps }">
@@ -553,9 +627,10 @@ onUnmounted(() => {
<template #prepend> <template #prepend>
<VIcon :icon="getThemeIcon" /> <VIcon :icon="getThemeIcon" />
</template> </template>
<VListItemTitle> <VListItemTitle>{{ t('common.theme') }}</VListItemTitle>
{{ themes.find(t => t.name === currentThemeName)?.title || t('common.theme') }} <VListItemSubtitle>
</VListItemTitle> {{ themes.find(t => t.name === currentThemeName)?.title || t('theme.auto') }}
</VListItemSubtitle>
<template #append> <template #append>
<VIcon icon="mdi-chevron-right" size="small" /> <VIcon icon="mdi-chevron-right" size="small" />
</template> </template>
+2
View File
@@ -30,6 +30,7 @@ export default {
saving: 'Saving', saving: 'Saving',
reset: 'Reset', reset: 'Reset',
theme: 'Theme', theme: 'Theme',
uiMode: 'UI Layout',
language: 'Language', language: 'Language',
pleaseWait: 'Please wait...', pleaseWait: 'Please wait...',
viewDetails: 'View Details', viewDetails: 'View Details',
@@ -131,6 +132,7 @@ export default {
light: 'Light', light: 'Light',
dark: 'Dark', dark: 'Dark',
auto: 'Follow System', auto: 'Follow System',
autoUI: 'Auto Layout',
transparent: 'Transparent', transparent: 'Transparent',
purple: 'Purple', purple: 'Purple',
custom: 'Custom Style', custom: 'Custom Style',
+2
View File
@@ -30,6 +30,7 @@ export default {
saving: '保存中', saving: '保存中',
reset: '重置', reset: '重置',
theme: '主题', theme: '主题',
uiMode: '界面布局',
language: '语言', language: '语言',
pleaseWait: '请稍候...', pleaseWait: '请稍候...',
viewDetails: '查看详情', viewDetails: '查看详情',
@@ -131,6 +132,7 @@ export default {
light: '浅色', light: '浅色',
dark: '深色', dark: '深色',
auto: '跟随系统', auto: '跟随系统',
autoUI: '自动布局',
transparent: '透明', transparent: '透明',
purple: '幻紫', purple: '幻紫',
custom: '附加样式', custom: '附加样式',
+2
View File
@@ -30,6 +30,7 @@ export default {
saving: '保存中', saving: '保存中',
reset: '重置', reset: '重置',
theme: '主題', theme: '主題',
uiMode: '界面佈局',
language: '語言', language: '語言',
pleaseWait: '請稍候...', pleaseWait: '請稍候...',
viewDetails: '查看詳情', viewDetails: '查看詳情',
@@ -131,6 +132,7 @@ export default {
light: '淺色', light: '淺色',
dark: '深色', dark: '深色',
auto: '跟隨系統', auto: '跟隨系統',
autoUI: '自動佈局',
transparent: '透明', transparent: '透明',
purple: '幻紫', purple: '幻紫',
custom: '附加樣式', custom: '附加樣式',