diff --git a/src/store/slices/deviceSlice.js b/src/store/slices/deviceSlice.js index f92406da..72ee9403 100644 --- a/src/store/slices/deviceSlice.js +++ b/src/store/slices/deviceSlice.js @@ -2,27 +2,81 @@ import { createSlice } from '@reduxjs/toolkit'; /** - * 检测当前设备是否为移动设备 - * - * 判断逻辑: - * 1. User Agent 检测(移动设备标识) - * 2. 屏幕宽度检测(<= 768px) - * 3. 触摸屏检测(支持触摸事件) - * - * @returns {boolean} true 表示移动设备,false 表示桌面设备 + * 设备类型枚举 */ -const detectIsMobile = () => { - const userAgent = navigator.userAgent || navigator.vendor || window.opera; - const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i; - const isMobileUA = mobileRegex.test(userAgent); - const isMobileWidth = window.innerWidth <= 768; - const hasTouchScreen = 'ontouchstart' in window || navigator.maxTouchPoints > 0; - - return isMobileUA || (isMobileWidth && hasTouchScreen); +export const DeviceType = { + MOBILE: 'mobile', + TABLET: 'tablet', + DESKTOP: 'desktop', }; +/** + * 检测设备类型 + * + * 判断逻辑: + * - Mobile: 手机设备(iPhone、Android 手机等,宽度 <= 768px) + * - Tablet: 平板设备(iPad、Android 平板等,宽度 769px - 1024px) + * - Desktop: 桌面设备(PC、Mac 等,宽度 > 1024px) + * + * @returns {{ isMobile: boolean, isTablet: boolean, isDesktop: boolean, deviceType: string }} + */ +const detectDeviceType = () => { + const userAgent = navigator.userAgent || navigator.vendor || window.opera; + const screenWidth = window.innerWidth; + + // iPad 检测(包括 iPadOS 13+ 的 Safari,其 UA 不再包含 iPad) + const isIPad = + /iPad/i.test(userAgent) || + (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); + + // Android 平板检测(Android 设备但 UA 不包含 Mobile) + const isAndroidTablet = /Android/i.test(userAgent) && !/Mobile/i.test(userAgent); + + // 手机 UA 检测(排除平板) + const mobileRegex = /iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i; + const isAndroidPhone = /Android/i.test(userAgent) && /Mobile/i.test(userAgent); + const isMobileUA = mobileRegex.test(userAgent) || isAndroidPhone; + + // 触摸屏检测 + const hasTouchScreen = 'ontouchstart' in window || navigator.maxTouchPoints > 0; + + // 综合判断设备类型 + let isMobile = false; + let isTablet = false; + let isDesktop = false; + + if (isIPad || isAndroidTablet) { + // UA 明确是平板 + isTablet = true; + } else if (isMobileUA) { + // UA 明确是手机 + isMobile = true; + } else if (screenWidth <= 768 && hasTouchScreen) { + // 小屏幕 + 触摸屏 = 手机 + isMobile = true; + } else if (screenWidth > 768 && screenWidth <= 1024 && hasTouchScreen) { + // 中等屏幕 + 触摸屏 = 平板 + isTablet = true; + } else { + // 其他情况为桌面设备 + isDesktop = true; + } + + // 确定设备类型字符串 + let deviceType = DeviceType.DESKTOP; + if (isMobile) deviceType = DeviceType.MOBILE; + else if (isTablet) deviceType = DeviceType.TABLET; + + return { isMobile, isTablet, isDesktop, deviceType }; +}; + +const initialDeviceState = detectDeviceType(); + const initialState = { - isMobile: detectIsMobile(), + isMobile: initialDeviceState.isMobile, + isTablet: initialDeviceState.isTablet, + isDesktop: initialDeviceState.isDesktop, + deviceType: initialDeviceState.deviceType, }; const deviceSlice = createSlice({ @@ -37,7 +91,11 @@ const deviceSlice = createSlice({ * - 屏幕方向变化时调用(orientationchange) */ updateScreenSize: (state) => { - state.isMobile = detectIsMobile(); + const { isMobile, isTablet, isDesktop, deviceType } = detectDeviceType(); + state.isMobile = isMobile; + state.isTablet = isTablet; + state.isDesktop = isDesktop; + state.deviceType = deviceType; }, }, }); @@ -47,6 +105,9 @@ export const { updateScreenSize } = deviceSlice.actions; // Selectors export const selectIsMobile = (state) => state.device.isMobile; +export const selectIsTablet = (state) => state.device.isTablet; +export const selectIsDesktop = (state) => state.device.isDesktop; +export const selectDeviceType = (state) => state.device.deviceType; // Reducer export default deviceSlice.reducer;