231 lines
4.6 KiB
JavaScript
231 lines
4.6 KiB
JavaScript
/**
|
||
* 移动端工具函数
|
||
*/
|
||
|
||
/**
|
||
* 检测是否为移动设备
|
||
*/
|
||
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
|
||
}
|
||
|