Kaynağa Gözat

fix: 禁止用户缩放网页

zhanghaijun 1 yıl önce
ebeveyn
işleme
ea96038692

+ 10 - 1
index.html

@@ -3,7 +3,16 @@
   <head>
     <title>Art Design Pro</title>
     <meta charset="UTF-8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0" /> -->
+    <!-- 禁止缩放页面 -->
+     <!-- 
+         width=device-width  设置页面的宽度与设备的屏幕宽度相同,确保网页在移动设备上能正确显示。
+         initial-scale=1.0   设置网页在移动设备上的初始缩放比例
+         maximum-scale=1.0   设置网页在移动设备上的最大缩放比例
+         minimum-scale=1.0   设置网页在移动设备上的最小缩放比例
+         user-scalable=no    禁用用户缩放功能
+     -->
+     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
     <meta
       name="description"
       content="Art Design Pro - A modern admin dashboard template built with Vue 3, TypeScript, and Element Plus."

+ 26 - 138
src/components/core/layouts/art-settings-panel/composables/useSettingsConfig.ts

@@ -12,42 +12,18 @@ export function useSettingsConfig() {
 
   // 标签页风格选项
   const tabStyleOptions = computed(() => [
-    {
-      value: 'tab-default',
-      label: t('setting.tabStyle.default')
-    },
-    {
-      value: 'tab-card',
-      label: t('setting.tabStyle.card')
-    },
-    {
-      value: 'tab-google',
-      label: t('setting.tabStyle.google')
-    }
+    { value: 'tab-default', label: t('setting.tabStyle.default') },
+    { value: 'tab-card', label: t('setting.tabStyle.card') },
+    { value: 'tab-google', label: t('setting.tabStyle.google') }
   ])
 
   // 页面切换动画选项
   const pageTransitionOptions = computed(() => [
-    {
-      value: '',
-      label: t('setting.transition.list.none')
-    },
-    {
-      value: 'fade',
-      label: t('setting.transition.list.fade')
-    },
-    {
-      value: 'slide-left',
-      label: t('setting.transition.list.slideLeft')
-    },
-    {
-      value: 'slide-bottom',
-      label: t('setting.transition.list.slideBottom')
-    },
-    {
-      value: 'slide-top',
-      label: t('setting.transition.list.slideTop')
-    }
+    { value: '', label: t('setting.transition.list.none') },
+    { value: 'fade', label: t('setting.transition.list.fade') },
+    { value: 'slide-left', label: t('setting.transition.list.slideLeft') },
+    { value: 'slide-bottom', label: t('setting.transition.list.slideBottom') },
+    { value: 'slide-top', label: t('setting.transition.list.slideTop') }
   ])
 
   // 圆角大小选项
@@ -61,40 +37,22 @@ export function useSettingsConfig() {
 
   // 容器宽度选项
   const containerWidthOptions = computed(() => [
-    {
-      value: ContainerWidthEnum.FULL,
-      label: t('setting.container.list[0]'),
-      icon: '&#xe694;'
-    },
-    {
-      value: ContainerWidthEnum.BOXED,
-      label: t('setting.container.list[1]'),
-      icon: '&#xe6de;'
-    }
+    { value: ContainerWidthEnum.FULL, label: t('setting.container.list[0]'), icon: '&#xe694;' },
+    { value: ContainerWidthEnum.BOXED, label: t('setting.container.list[1]'), icon: '&#xe6de;' }
   ])
 
   // 盒子样式选项
   const boxStyleOptions = computed(() => [
-    {
-      value: 'border-mode',
-      label: t('setting.box.list[0]'),
-      type: 'border-mode' as const
-    },
-    {
-      value: 'shadow-mode',
-      label: t('setting.box.list[1]'),
-      type: 'shadow-mode' as const
-    }
+    { value: 'border-mode', label: t('setting.box.list[0]'), type: 'border-mode' as const },
+    { value: 'shadow-mode', label: t('setting.box.list[1]'), type: 'shadow-mode' as const }
   ])
 
   // 从配置文件获取的选项
   const configOptions = {
     // 主题色彩选项
     mainColors: AppConfig.systemMainColor,
-
     // 主题风格选项
     themeList: AppConfig.settingThemeList,
-
     // 菜单布局选项
     menuLayoutList: AppConfig.menuLayoutList
   }
@@ -103,77 +61,16 @@ export function useSettingsConfig() {
   const basicSettingsConfig = computed(() => {
     // 定义所有基础设置项
     const allSettings = [
-      {
-        key: 'showWorkTab',
-        label: t('setting.basics.list.multiTab'),
-        type: 'switch' as const,
-        handler: 'workTab',
-        headerBarKey: null // 不依赖headerBar配置
-      },
-      {
-        key: 'uniqueOpened',
-        label: t('setting.basics.list.accordion'),
-        type: 'switch' as const,
-        handler: 'uniqueOpened',
-        headerBarKey: null // 不依赖headerBar配置
-      },
-      {
-        key: 'showMenuButton',
-        label: t('setting.basics.list.collapseSidebar'),
-        type: 'switch' as const,
-        handler: 'menuButton',
-        headerBarKey: 'menuButton' as const
-      },
-      {
-        key: 'showFastEnter',
-        label: t('setting.basics.list.fastEnter'),
-        type: 'switch' as const,
-        handler: 'fastEnter',
-        headerBarKey: 'fastEnter' as const
-      },
-      {
-        key: 'showRefreshButton',
-        label: t('setting.basics.list.reloadPage'),
-        type: 'switch' as const,
-        handler: 'refreshButton',
-        headerBarKey: 'refreshButton' as const
-      },
-      {
-        key: 'showCrumbs',
-        label: t('setting.basics.list.breadcrumb'),
-        type: 'switch' as const,
-        handler: 'crumbs',
-        mobileHide: true,
-        headerBarKey: 'breadcrumb' as const
-      },
-      {
-        key: 'showLanguage',
-        label: t('setting.basics.list.language'),
-        type: 'switch' as const,
-        handler: 'language',
-        headerBarKey: 'language' as const
-      },
-      {
-        key: 'showNprogress',
-        label: t('setting.basics.list.progressBar'),
-        type: 'switch' as const,
-        handler: 'nprogress',
-        headerBarKey: null // 不依赖headerBar配置
-      },
-      {
-        key: 'colorWeak',
-        label: t('setting.basics.list.weakMode'),
-        type: 'switch' as const,
-        handler: 'colorWeak',
-        headerBarKey: null // 不依赖headerBar配置
-      },
-      {
-        key: 'watermarkVisible',
-        label: t('setting.basics.list.watermark'),
-        type: 'switch' as const,
-        handler: 'watermark',
-        headerBarKey: null // 不依赖headerBar配置
-      },
+      { key: 'showWorkTab', label: t('setting.basics.list.multiTab'), type: 'switch' as const, handler: 'workTab', headerBarKey: null },
+      // { key: 'uniqueOpened', label: t('setting.basics.list.accordion'), type: 'switch' as const, handler: 'uniqueOpened', headerBarKey: null },
+      // { key: 'showMenuButton', label: t('setting.basics.list.collapseSidebar'), type: 'switch' as const, handler: 'menuButton', headerBarKey: 'menuButton' as const },
+      // { key: 'showFastEnter', label: t('setting.basics.list.fastEnter'), type: 'switch' as const, handler: 'fastEnter', headerBarKey: 'fastEnter' as const },
+      { key: 'showRefreshButton', label: t('setting.basics.list.reloadPage'), type: 'switch' as const, handler: 'refreshButton', headerBarKey: 'refreshButton' as const },
+      // { key: 'showCrumbs', label: t('setting.basics.list.breadcrumb'), type: 'switch' as const, handler: 'crumbs', mobileHide: true, headerBarKey: 'breadcrumb' as const },
+      // { key: 'showLanguage', label: t('setting.basics.list.language'), type: 'switch' as const, handler: 'language', headerBarKey: 'language' as const },
+      { key: 'showNprogress', label: t('setting.basics.list.progressBar'), type: 'switch' as const, handler: 'nprogress', headerBarKey: null },
+      { key: 'colorWeak', label: t('setting.basics.list.weakMode'), type: 'switch' as const, handler: 'colorWeak', headerBarKey: null },
+      // { key: 'watermarkVisible', label: t('setting.basics.list.watermark'), type: 'switch' as const, handler: 'watermark', headerBarKey: null },
       {
         key: 'menuOpenWidth',
         label: t('setting.basics.list.menuWidth'),
@@ -184,17 +81,9 @@ export function useSettingsConfig() {
         step: 10,
         style: { width: '120px' },
         controlsPosition: 'right' as const,
-        headerBarKey: null // 不依赖headerBar配置
-      },
-      {
-        key: 'tabStyle',
-        label: t('setting.basics.list.tabStyle'),
-        type: 'select' as const,
-        handler: 'tabStyle',
-        options: tabStyleOptions.value,
-        style: { width: '120px' },
-        headerBarKey: null // 不依赖headerBar配置
+        headerBarKey: null
       },
+      { key: 'tabStyle', label: t('setting.basics.list.tabStyle'), type: 'select' as const, handler: 'tabStyle', options: tabStyleOptions.value, style: { width: '120px' }, headerBarKey: null },
       {
         key: 'pageTransition',
         label: t('setting.basics.list.pageTransition'),
@@ -202,7 +91,7 @@ export function useSettingsConfig() {
         handler: 'pageTransition',
         options: pageTransitionOptions.value,
         style: { width: '120px' },
-        headerBarKey: null // 不依赖headerBar配置
+        headerBarKey: null
       },
       {
         key: 'customRadius',
@@ -211,10 +100,9 @@ export function useSettingsConfig() {
         handler: 'customRadius',
         options: customRadiusOptions,
         style: { width: '120px' },
-        headerBarKey: null // 不依赖headerBar配置
+        headerBarKey: null
       }
     ]
-
     // 根据 headerBarConfig 过滤设置项
     return (
       allSettings

+ 1 - 6
src/components/core/layouts/art-settings-panel/widget/MenuLayoutSettings.vue

@@ -2,12 +2,7 @@
   <div v-if="width > 1000">
     <SectionTitle :title="$t('setting.menuType.title')" />
     <div class="setting-box-wrap">
-      <div
-        class="setting-item"
-        v-for="(item, index) in configOptions.menuLayoutList"
-        :key="item.value"
-        @click="switchMenuLayouts(item.value)"
-      >
+      <div class="setting-item" v-for="(item, index) in configOptions.menuLayoutList" :key="item.value" @click="switchMenuLayouts(item.value)">
         <div class="box" :class="{ 'is-active': item.value === menuType, 'mt-16': index > 2 }">
           <img :src="item.img" />
         </div>

+ 2 - 0
src/main.ts

@@ -16,7 +16,9 @@ import '@icons/system/iconfont.css'                 // 系统图标
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 import { setupGlobDirectives } from './directives'
 import language from './locales'
+import { disableUserZoom } from './utils/browser/disable-zoom';
 
+disableUserZoom();
 document.addEventListener('touchstart',  function () {},  { passive: false })
 
 const app = createApp(App)

+ 1 - 1
src/store/modules/setting.ts

@@ -18,7 +18,7 @@ export const useSettingStore = defineStore(
   () => {
     // 菜单相关设置
     /** 菜单类型 */
-    const menuType = ref(MenuTypeEnum.LEFT)
+    const menuType = ref(MenuTypeEnum.TOP)
     /** 菜单展开宽度 */
     const menuOpenWidth = ref(defaultMenuWidth)
     /** 菜单是否展开 */

+ 1 - 3
src/utils/browser/cookie.ts

@@ -6,9 +6,7 @@
 export function setCookie(key: string, value: string, expireDays: number): void {
   const date = new Date()
   date.setDate(date.getDate() + expireDays)
-  document.cookie = `${key}=${encodeURIComponent(value)}${
-    expireDays ? `;expires=${date.toUTCString()}` : ''
-  }`
+  document.cookie = `${key}=${encodeURIComponent(value)}${expireDays ? `;expires=${date.toUTCString()}` : ''}`
 }
 
 // 删除 Cookie

+ 49 - 0
src/utils/browser/disable-zoom.ts

@@ -0,0 +1,49 @@
+// src/disable-zoom.ts
+export function disableUserZoom() {
+  // 阻止 Ctrl/Cmd + 滚轮 缩放
+  const wheelHandler = (e: WheelEvent) => {
+    if ((e.ctrlKey || (e.metaKey && navigator.platform?.includes('Mac'))) && Math.abs(e.deltaY) > 0) {
+      e.preventDefault()
+    }
+  }
+
+  // 阻止 Ctrl/Cmd + +/- / 0
+  const keyHandler = (e: KeyboardEvent) => {
+    const ctrl = e.ctrlKey || e.metaKey
+    if (!ctrl) return
+    // 字符识别:'+', '-', '=', '0' 以及小键盘 + -
+    if (e.key === '+' || e.key === '-' || e.key === '=' || e.key === '0' || e.key === 'Add' || e.key === 'Subtract') {
+      e.preventDefault()
+    }
+  }
+
+  // iOS Safari 的捏合手势(早期支持 gesturestart/gesturechange)
+  const gestureHandler = (e: Event) => {
+    e.preventDefault()
+  }
+
+  // Prevent pinch-to-zoom via touch (mobile)
+  // 注意:touch-action: none 可以阻止双指捏合,但会影响滚动交互
+  const touchHandler = (e: TouchEvent) => {
+    if (e.touches && e.touches.length > 1) {
+      e.preventDefault()
+    }
+  }
+
+  window.addEventListener('wheel', wheelHandler, { passive: false })
+  window.addEventListener('keydown', keyHandler, { passive: false })
+  // 若浏览器支持 gesture events(部分 iOS),禁止之
+  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+  // @ts-ignore
+  window.addEventListener?.('gesturestart', gestureHandler as EventListener, { passive: false })
+  window.addEventListener('touchstart', touchHandler as EventListener, { passive: false })
+
+  return () => {
+    window.removeEventListener('wheel', wheelHandler)
+    window.removeEventListener('keydown', keyHandler)
+    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+    // @ts-ignore
+    window.removeEventListener?.('gesturestart', gestureHandler as EventListener)
+    window.removeEventListener('touchstart', touchHandler as EventListener)
+  }
+}