fix: adjust dialog fullscreen breakpoint by width

This commit is contained in:
jxxghp
2026-06-30 14:17:04 +08:00
parent 8c1eb3e981
commit 3f525c6457
2 changed files with 91 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
import { defineComponent, h } from 'vue'
import { useDisplay } from 'vuetify'
import { VDialog as VuetifyDialog } from 'vuetify/components/VDialog'
const DEFAULT_ROOT_FONT_SIZE = 16
/**
* 判断传入的 fullscreen 属性是否已请求全屏展示。
*/
function isFullscreenRequested(value: unknown) {
return value !== undefined && value !== null && value !== false
}
/**
* 获取文档根字号,用于把 rem/em 宽度限制换算成像素。
*/
function getRootFontSize() {
if (typeof window === 'undefined') return DEFAULT_ROOT_FONT_SIZE
const rootFontSize = Number.parseFloat(window.getComputedStyle(document.documentElement).fontSize)
return Number.isFinite(rootFontSize) ? rootFontSize : DEFAULT_ROOT_FONT_SIZE
}
/**
* 将弹窗宽度限制换算成像素,无法可靠解析的复杂表达式返回空值。
*/
function parseDialogWidthLimit(value: unknown, viewportWidth: number) {
if (typeof value === 'number') return Number.isFinite(value) ? value : undefined
if (typeof value !== 'string') return undefined
const trimmedValue = value.trim()
if (!trimmedValue) return undefined
const numericValue = Number(trimmedValue)
if (Number.isFinite(numericValue)) return numericValue
const lengthMatch = trimmedValue.match(/^(-?\d*\.?\d+)(px|rem|em|vw|%)$/i)
if (!lengthMatch) return undefined
const [, rawAmount, rawUnit] = lengthMatch
const amount = Number(rawAmount)
if (!Number.isFinite(amount)) return undefined
const unit = rawUnit.toLowerCase()
if (unit === 'px') return amount
if (unit === 'rem' || unit === 'em') return amount * getRootFontSize()
if (unit === 'vw') return (viewportWidth * amount) / 100
if (unit === '%') return (viewportWidth * amount) / 100
return undefined
}
/**
* 根据 maxWidth 与 width 的有效上限,收窄 fullscreen 的生效宽度。
*/
function resolveFullscreen(
fullscreen: unknown,
maxWidth: unknown,
width: unknown,
viewportWidth: number,
) {
if (!isFullscreenRequested(fullscreen)) return false
const widthLimits = [maxWidth, width]
.map(value => parseDialogWidthLimit(value, viewportWidth))
.filter((value): value is number => value !== undefined)
if (!widthLimits.length) return true
return viewportWidth <= Math.min(...widthLimits)
}
export default defineComponent({
name: 'AppDialog',
inheritAttrs: false,
/**
* 渲染项目统一弹窗,并在宽度上限小于移动全屏断点时收窄 fullscreen 条件。
*/
setup(_, { attrs, slots }) {
const display = useDisplay()
return () => {
const maxWidth = attrs.maxWidth ?? attrs['max-width']
const resolvedFullscreen = resolveFullscreen(attrs.fullscreen, maxWidth, attrs.width, display.width.value)
return h(VuetifyDialog, { ...attrs, fullscreen: resolvedFullscreen }, slots)
}
},
})

View File

@@ -2,6 +2,7 @@ import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import { VBtn } from 'vuetify/components/VBtn'
import * as labsComponents from 'vuetify/labs/components'
import AppDialog from '@/components/dialog/AppDialog'
import defaults from './defaults'
import { icons } from './icons'
import theme from './theme'
@@ -15,6 +16,7 @@ export default createVuetify({
theme,
components: {
...components,
VDialog: AppDialog,
...labsComponents,
},
})