1
This commit is contained in:
230
src/utils/mobile.js
Normal file
230
src/utils/mobile.js
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* 移动端工具函数
|
||||
*/
|
||||
|
||||
/**
|
||||
* 检测是否为移动设备
|
||||
*/
|
||||
export function isMobile() {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||
|
||||
window.innerWidth < 768
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为iOS设备
|
||||
*/
|
||||
export function isIOS() {
|
||||
return /iPad|iPhone|iPod/.test(navigator.userAgent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为Android设备
|
||||
*/
|
||||
export function isAndroid() {
|
||||
return /Android/.test(navigator.userAgent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备类型
|
||||
*/
|
||||
export function getDeviceType() {
|
||||
if (isMobile()) {
|
||||
if (isIOS()) return 'ios'
|
||||
if (isAndroid()) return 'android'
|
||||
return 'mobile'
|
||||
}
|
||||
return 'desktop'
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置移动端视口
|
||||
*/
|
||||
export function setMobileViewport() {
|
||||
if (isMobile()) {
|
||||
const viewport = document.querySelector('meta[name="viewport"]')
|
||||
if (viewport) {
|
||||
viewport.setAttribute('content', 'width=device-width, initial-scale=1, maximum-scale=5, minimum-scale=1, user-scalable=yes, viewport-fit=cover')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 防止iOS双击缩放
|
||||
*/
|
||||
export function preventDoubleTapZoom() {
|
||||
if (isIOS()) {
|
||||
let lastTouchEnd = 0
|
||||
document.addEventListener('touchend', (event) => {
|
||||
const now = Date.now()
|
||||
if (now - lastTouchEnd <= 300) {
|
||||
event.preventDefault()
|
||||
}
|
||||
lastTouchEnd = now
|
||||
}, false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 优化移动端滚动
|
||||
*/
|
||||
export function optimizeMobileScroll() {
|
||||
if (isMobile()) {
|
||||
// 添加平滑滚动
|
||||
document.documentElement.style.scrollBehavior = 'smooth'
|
||||
|
||||
// 优化触摸滚动
|
||||
const style = document.createElement('style')
|
||||
style.textContent = `
|
||||
* {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置安全区域适配(iOS刘海屏)
|
||||
*/
|
||||
export function setSafeArea() {
|
||||
if (isIOS()) {
|
||||
const style = document.createElement('style')
|
||||
style.textContent = `
|
||||
.safe-area-top {
|
||||
padding-top: env(safe-area-inset-top);
|
||||
}
|
||||
.safe-area-bottom {
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
.safe-area-left {
|
||||
padding-left: env(safe-area-inset-left);
|
||||
}
|
||||
.safe-area-right {
|
||||
padding-right: env(safe-area-inset-right);
|
||||
}
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动端初始化
|
||||
*/
|
||||
export function initMobile() {
|
||||
setMobileViewport()
|
||||
preventDoubleTapZoom()
|
||||
optimizeMobileScroll()
|
||||
setSafeArea()
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化移动端表格数据为卡片数据
|
||||
*/
|
||||
export function formatTableToCards(tableData, columns) {
|
||||
return tableData.map(row => {
|
||||
const card = {}
|
||||
columns.forEach(column => {
|
||||
if (column.visible !== false) {
|
||||
const value = column.prop ? getNestedValue(row, column.prop) : ''
|
||||
card[column.prop || column.key] = {
|
||||
label: column.label,
|
||||
value: column.formatter ? column.formatter(row, column, value) : value,
|
||||
type: column.type || 'text'
|
||||
}
|
||||
}
|
||||
})
|
||||
return {
|
||||
...row,
|
||||
_cardData: card
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取嵌套对象值
|
||||
*/
|
||||
function getNestedValue(obj, path) {
|
||||
return path.split('.').reduce((o, p) => o && o[p], obj)
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动端表格列配置
|
||||
*/
|
||||
export function getMobileTableColumns(columns) {
|
||||
return columns
|
||||
.filter(col => col.visible !== false)
|
||||
.map(col => ({
|
||||
...col,
|
||||
label: col.label || col.prop,
|
||||
prop: col.prop || col.key
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* 防抖函数
|
||||
*/
|
||||
export function debounce(func, wait) {
|
||||
let timeout
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout)
|
||||
func(...args)
|
||||
}
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(later, wait)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 节流函数
|
||||
*/
|
||||
export function throttle(func, limit) {
|
||||
let inThrottle
|
||||
return function(...args) {
|
||||
if (!inThrottle) {
|
||||
func.apply(this, args)
|
||||
inThrottle = true
|
||||
setTimeout(() => inThrottle = false, limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动端图片懒加载
|
||||
*/
|
||||
export function lazyLoadImages() {
|
||||
if ('IntersectionObserver' in window) {
|
||||
const imageObserver = new IntersectionObserver((entries, observer) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const img = entry.target
|
||||
img.src = img.dataset.src
|
||||
img.classList.remove('lazy')
|
||||
imageObserver.unobserve(img)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
document.querySelectorAll('img.lazy').forEach(img => {
|
||||
imageObserver.observe(img)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
isMobile,
|
||||
isIOS,
|
||||
isAndroid,
|
||||
getDeviceType,
|
||||
setMobileViewport,
|
||||
preventDoubleTapZoom,
|
||||
optimizeMobileScroll,
|
||||
setSafeArea,
|
||||
initMobile,
|
||||
formatTableToCards,
|
||||
getMobileTableColumns,
|
||||
debounce,
|
||||
throttle,
|
||||
lazyLoadImages
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user