Files
template-MP-ts/common/utils/tool.ts
2025-09-29 14:05:25 +08:00

402 lines
9.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @ts-ignore
const baseUrl = import.meta.env.VITE_BASE_URL
// @ts-ignore
const assetsUrl = import.meta.env.VITE_ASSETSURL
interface StorageInfo {
keys: string[]
currentSize: number
limitSize: number
}
/**
* 工具类 - 提供常用的工具方法
* @class Tool
*/
class Tool {
ICON_TYPES: { [key: string]: number }
loadedFonts: Set<string>
constructor() {
// 图标类型映射
this.ICON_TYPES = {
NONE: 0,
SUCCESS: 1,
LOADING: 2,
}
// 字体加载状态缓存
this.loadedFonts = new Set()
}
/**
* 文字轻提示
* @param {string} str 提示文字
* @param {number} [icon=0] 提示icon (0: none, 1: success, 2: loading)
* @param {number} [duration=1500] 提示时间(毫秒)
*/
alert(str: string, icon: number = this.ICON_TYPES.NONE, duration: number = 1500): Promise<void> {
return new Promise((resolve, reject) => {
if (!str && str !== '0') {
console.warn('alert方法需要提供提示文字')
return
}
const iconMap: { [key: number]: string } = {
[this.ICON_TYPES.NONE]: 'none',
[this.ICON_TYPES.SUCCESS]: 'success',
[this.ICON_TYPES.LOADING]: 'loading',
}
// @ts-ignore
uni.showToast({
title: String(str),
// @ts-ignore
icon: iconMap[icon] || 'none',
mask: true,
duration,
success: () => {
setTimeout(resolve, duration)
},
fail: reject,
})
})
}
/**
* 显示loading加载
* @param {string} [title=' '] 加载文案
* @param {boolean} [mask=true] 是否显示遮罩
*/
loading(title: string = ' ', mask: boolean = true): void {
// @ts-ignore
uni.showLoading({ title, mask })
}
/**
* 关闭loading提示框
*/
hideLoading(): void {
// @ts-ignore
uni.hideLoading()
}
/**
* 统一处理URL格式确保以/开头
* @param {string} url 页面地址
* @returns {string} 格式化后的URL
* @private
*/
_formatUrl(url: string): string {
if (!url || typeof url !== 'string') {
throw new Error('URL必须是字符串')
}
return url.startsWith('/') ? url : `/${url}`
}
/**
* 可返回跳转(导航到新页面)
* @param {string} url 页面地址
*/
navigateTo(url: string): void {
const formattedUrl = this._formatUrl(url)
// @ts-ignore
uni.navigateTo({
url: formattedUrl,
fail: (err: any) => {
console.warn('navigateTo失败尝试switchTab:', err)
// @ts-ignore
uni.switchTab({ url: formattedUrl })
},
})
}
/**
* 不可返回跳转(重定向到新页面)
* @param {string} url 页面地址
*/
redirectTo(url: string): void {
// @ts-ignore
uni.redirectTo({ url: this._formatUrl(url) })
}
/**
* 清除页面栈跳转(重新启动到新页面)
* @param {string} url 页面地址
*/
reLaunch(url: string): void {
// @ts-ignore
uni.reLaunch({ url: this._formatUrl(url) })
}
/**
* 跳转tabBar页
* @param {string} url 页面地址
*/
switchTab(url: string): void {
// @ts-ignore
uni.switchTab({ url: this._formatUrl(url) })
}
/**
* 返回上一页面或指定页面
* @param {number} [delta=1] 返回的页面数
* @param {string} [fallbackUrl='/pages/index/index'] 无上一页时的回退地址
*/
navigateBack(delta: number = 1, fallbackUrl: string = '/pages/index/index'): void {
// @ts-ignore
const pages = getCurrentPages()
if (pages.length <= 1) {
console.warn('无上一页,使用回退地址')
// @ts-ignore
uni.reLaunch({ url: fallbackUrl })
} else {
// @ts-ignore
uni.navigateBack({ delta })
}
}
/**
* 操作本地缓存
* @param {string} key 缓存键值
* @param {any} [value] 缓存数据,不传则为读取
* @returns {any|undefined} 读取操作时返回数据
*/
storage(key: string, value?: any): any {
if (typeof key !== 'string') {
throw new Error('key必须是字符串')
}
// 设置操作
if (value !== undefined && value !== null) {
// @ts-ignore
uni.setStorageSync(key, value)
return
}
// 读取操作
if (key !== '#') {
// @ts-ignore
return uni.getStorageSync(key)
}
// 特殊操作
if (key === '#') {
// @ts-ignore
uni.clearStorageSync()
}
}
/**
* 删除指定缓存
* @param {string} key 要删除的缓存键
*/
removeStorage(key: string): void {
if (typeof key !== 'string') {
throw new Error('key必须是字符串')
}
// @ts-ignore
uni.removeStorageSync(key)
}
/**
* 获取缓存信息
* @returns {Object} 缓存信息
*/
getStorageInfo(): StorageInfo {
// @ts-ignore
return uni.getStorageInfoSync()
}
/**
* 复制文本到剪贴板
* @param {string} data 要复制的文本
* @returns {Promise<boolean>} 复制是否成功
*/
async copy(data: string): Promise<boolean> {
if (!data && data !== '0') {
this.alert('暂无内容')
return false
}
try {
await new Promise((resolve, reject) => {
// @ts-ignore
uni.setClipboardData({
data: String(data),
success: resolve,
fail: reject,
})
})
this.alert('复制成功')
return true
} catch (error) {
console.error('复制失败:', error)
this.alert('复制失败,请重试')
return false
}
}
/**
* 导入外部字体
* @param {string} fontName 字体文件名(不含路径)
* @returns {Promise<boolean>} 字体加载是否成功
*/
async loadFont(fontName: string): Promise<boolean> {
if (!fontName || typeof fontName !== 'string') {
throw new Error('字体名称必须是字符串')
}
// 检查是否已加载过
if (this.loadedFonts.has(fontName)) {
return true
}
try {
const fontFamily = fontName.replace(/\.[^/.]+$/, '') // 移除文件扩展名
await new Promise((resolve, reject) => {
// @ts-ignore
uni.loadFontFace({
family: fontFamily,
source: `url(${assetsUrl}${fontName})`,
global: true,
success: resolve,
fail: reject,
})
})
this.loadedFonts.add(fontName)
return true
} catch (error) {
console.error(`字体加载失败: ${fontName}`, error)
return false
}
}
/**
* 保存图片到相册
* @param {string} url 图片URL
* @returns {Promise<boolean>} 保存是否成功
*/
async saveImageToPhotos(url: string): Promise<boolean> {
if (!url) {
this.alert('图片地址不能为空')
return false
}
try {
// 检查权限
const { authSetting } = await new Promise((resolve, reject) => {
// @ts-ignore
uni.getSetting({
success: resolve,
fail: reject,
})
}) as any
if (!authSetting['scope.writePhotosAlbum']) {
// 请求权限
await new Promise((resolve, reject) => {
// @ts-ignore
uni.authorize({
scope: 'scope.writePhotosAlbum',
success: resolve,
fail: reject,
})
})
}
// 获取图片信息
const { path } = await new Promise((resolve, reject) => {
// @ts-ignore
uni.getImageInfo({
src: url,
success: resolve,
fail: reject,
})
}) as any
// 保存到相册
await new Promise((resolve, reject) => {
// @ts-ignore
uni.saveImageToPhotosAlbum({
filePath: path,
success: resolve,
fail: reject,
})
})
this.alert('已保存到相册')
return true
} catch (error: any) {
console.error('保存图片失败:', error)
if (error.errMsg && error.errMsg.includes('auth')) {
// 权限相关错误
await new Promise(resolve => {
// @ts-ignore
uni.showModal({
title: '保存失败',
content: '请开启访问手机相册权限',
showCancel: false,
success: resolve,
})
})
// @ts-ignore
uni.openSetting()
} else {
this.alert('保存失败,请重试')
}
return false
}
}
/**
* 微信支付
* @param {Object} paymentData 支付参数
* @returns {Promise<Object>} 支付结果
*/
requestPayment(paymentData: any): Promise<any> {
return new Promise((resolve, reject) => {
// @ts-ignore
uni.requestPayment({
provider: 'wxpay',
...paymentData,
success: resolve,
fail: reject,
})
})
}
upload(filePath: string): Promise<any> {
return new Promise((resolve, reject) => {
// @ts-ignore
uni.uploadFile({
url: `${baseUrl}file/upload`,
fileType: 'image',
header: {
Authorization: `Bearer ${this.storage('token')}`,
},
filePath,
name: 'file',
success: ({ data }: any) => {
resolve(JSON.parse(data))
},
fail: (error: any) => {
reject(error)
},
})
})
}
}
// 创建单例并导出
export default new Tool()