365 lines
8.3 KiB
JavaScript
365 lines
8.3 KiB
JavaScript
// UI辅助工具 - 全局用户体验优化
|
|
class UIHelper {
|
|
constructor() {
|
|
this.loadingCount = 0;
|
|
this.toastQueue = [];
|
|
this.isShowingToast = false;
|
|
}
|
|
|
|
// 🔥 ===== 加载状态管理 =====
|
|
|
|
// 显示加载
|
|
showLoading(title = '加载中...', mask = true) {
|
|
this.loadingCount++;
|
|
|
|
if (this.loadingCount === 1) {
|
|
wx.showLoading({
|
|
title: title,
|
|
mask: mask
|
|
});
|
|
}
|
|
}
|
|
|
|
// 隐藏加载
|
|
hideLoading() {
|
|
this.loadingCount = Math.max(0, this.loadingCount - 1);
|
|
|
|
if (this.loadingCount === 0) {
|
|
wx.hideLoading();
|
|
}
|
|
}
|
|
|
|
// 强制隐藏加载
|
|
forceHideLoading() {
|
|
this.loadingCount = 0;
|
|
wx.hideLoading();
|
|
}
|
|
|
|
// 🔥 ===== 消息提示管理 =====
|
|
|
|
// 显示成功消息
|
|
showSuccess(title, duration = 2000) {
|
|
this.showToast({
|
|
title: title,
|
|
icon: 'success',
|
|
duration: duration
|
|
});
|
|
}
|
|
|
|
// 显示错误消息
|
|
showError(title, duration = 3000) {
|
|
this.showToast({
|
|
title: title,
|
|
icon: 'error',
|
|
duration: duration
|
|
});
|
|
}
|
|
|
|
// 显示警告消息
|
|
showWarning(title, duration = 2500) {
|
|
this.showToast({
|
|
title: title,
|
|
icon: 'none',
|
|
duration: duration
|
|
});
|
|
}
|
|
|
|
// 显示信息消息
|
|
showInfo(title, duration = 2000) {
|
|
this.showToast({
|
|
title: title,
|
|
icon: 'none',
|
|
duration: duration
|
|
});
|
|
}
|
|
|
|
// 队列化Toast显示
|
|
showToast(options) {
|
|
this.toastQueue.push(options);
|
|
this.processToastQueue();
|
|
}
|
|
|
|
// 处理Toast队列
|
|
processToastQueue() {
|
|
if (this.isShowingToast || this.toastQueue.length === 0) {
|
|
return;
|
|
}
|
|
|
|
this.isShowingToast = true;
|
|
const options = this.toastQueue.shift();
|
|
|
|
wx.showToast({
|
|
...options,
|
|
success: () => {
|
|
setTimeout(() => {
|
|
this.isShowingToast = false;
|
|
this.processToastQueue();
|
|
}, options.duration || 2000);
|
|
},
|
|
fail: () => {
|
|
this.isShowingToast = false;
|
|
this.processToastQueue();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 🔥 ===== 模态框管理 =====
|
|
|
|
// 显示确认对话框
|
|
showConfirm(options) {
|
|
return new Promise((resolve) => {
|
|
wx.showModal({
|
|
title: options.title || '提示',
|
|
content: options.content || '',
|
|
showCancel: options.showCancel !== false,
|
|
cancelText: options.cancelText || '取消',
|
|
confirmText: options.confirmText || '确定',
|
|
cancelColor: options.cancelColor || '#666666',
|
|
confirmColor: options.confirmColor || '#4CAF50',
|
|
success: (res) => {
|
|
resolve(res.confirm);
|
|
},
|
|
fail: () => {
|
|
resolve(false);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 显示操作菜单
|
|
showActionSheet(options) {
|
|
return new Promise((resolve) => {
|
|
wx.showActionSheet({
|
|
itemList: options.itemList || [],
|
|
itemColor: options.itemColor || '#000000',
|
|
success: (res) => {
|
|
resolve(res.tapIndex);
|
|
},
|
|
fail: () => {
|
|
resolve(-1);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 🔥 ===== 网络状态管理 =====
|
|
|
|
// 检查网络状态
|
|
async checkNetworkStatus() {
|
|
try {
|
|
const networkInfo = await this.getNetworkType();
|
|
|
|
if (networkInfo.networkType === 'none') {
|
|
this.showError('网络连接不可用,请检查网络设置');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error('检查网络状态失败:', error);
|
|
return true; // 默认认为网络可用
|
|
}
|
|
}
|
|
|
|
// 获取网络类型
|
|
getNetworkType() {
|
|
return new Promise((resolve, reject) => {
|
|
wx.getNetworkType({
|
|
success: resolve,
|
|
fail: reject
|
|
});
|
|
});
|
|
}
|
|
|
|
// 🔥 ===== 页面导航管理 =====
|
|
|
|
// 安全导航到页面
|
|
navigateTo(url, options = {}) {
|
|
// 检查URL格式
|
|
if (!url || typeof url !== 'string') {
|
|
this.showError('页面地址无效');
|
|
return Promise.reject(new Error('Invalid URL'));
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
wx.navigateTo({
|
|
url: url,
|
|
success: resolve,
|
|
fail: (error) => {
|
|
console.error('页面导航失败:', error);
|
|
|
|
// 如果是页面栈满了,尝试重定向
|
|
if (error.errMsg && error.errMsg.includes('limit exceed')) {
|
|
wx.redirectTo({
|
|
url: url,
|
|
success: resolve,
|
|
fail: reject
|
|
});
|
|
} else {
|
|
this.showError('页面跳转失败');
|
|
reject(error);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 安全重定向到页面
|
|
redirectTo(url) {
|
|
return new Promise((resolve, reject) => {
|
|
wx.redirectTo({
|
|
url: url,
|
|
success: resolve,
|
|
fail: (error) => {
|
|
console.error('页面重定向失败:', error);
|
|
this.showError('页面跳转失败');
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 安全重启到页面
|
|
reLaunch(url) {
|
|
return new Promise((resolve, reject) => {
|
|
wx.reLaunch({
|
|
url: url,
|
|
success: resolve,
|
|
fail: (error) => {
|
|
console.error('页面重启失败:', error);
|
|
this.showError('页面跳转失败');
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 🔥 ===== 错误处理 =====
|
|
|
|
// 处理API错误
|
|
handleApiError(error, defaultMessage = '操作失败,请重试') {
|
|
console.error('API错误:', error);
|
|
|
|
let message = defaultMessage;
|
|
|
|
if (error && error.message) {
|
|
message = error.message;
|
|
} else if (typeof error === 'string') {
|
|
message = error;
|
|
}
|
|
|
|
// 特殊错误处理
|
|
if (message.includes('网络')) {
|
|
this.showError('网络连接异常,请检查网络设置');
|
|
} else if (message.includes('登录') || message.includes('认证') || message.includes('token')) {
|
|
this.showError('登录已过期,请重新登录');
|
|
// 可以在这里触发重新登录逻辑
|
|
} else {
|
|
this.showError(message);
|
|
}
|
|
}
|
|
|
|
// 🔥 ===== 工具方法 =====
|
|
|
|
// 防抖函数
|
|
debounce(func, wait) {
|
|
let timeout;
|
|
return function executedFunction(...args) {
|
|
const later = () => {
|
|
clearTimeout(timeout);
|
|
func(...args);
|
|
};
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(later, wait);
|
|
};
|
|
}
|
|
|
|
// 节流函数
|
|
throttle(func, limit) {
|
|
let inThrottle;
|
|
return function executedFunction(...args) {
|
|
if (!inThrottle) {
|
|
func.apply(this, args);
|
|
inThrottle = true;
|
|
setTimeout(() => inThrottle = false, limit);
|
|
}
|
|
};
|
|
}
|
|
|
|
// 格式化文件大小
|
|
formatFileSize(bytes) {
|
|
if (bytes === 0) return '0 B';
|
|
|
|
const k = 1024;
|
|
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
}
|
|
|
|
// 格式化时间
|
|
formatTime(timestamp) {
|
|
const now = new Date();
|
|
const time = new Date(timestamp);
|
|
const diff = now - time;
|
|
|
|
// 一分钟内
|
|
if (diff < 60000) {
|
|
return '刚刚';
|
|
}
|
|
|
|
// 一小时内
|
|
if (diff < 3600000) {
|
|
return Math.floor(diff / 60000) + '分钟前';
|
|
}
|
|
|
|
// 今天
|
|
if (now.toDateString() === time.toDateString()) {
|
|
return time.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
|
|
}
|
|
|
|
// 昨天
|
|
const yesterday = new Date(now);
|
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
if (yesterday.toDateString() === time.toDateString()) {
|
|
return '昨天 ' + time.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
|
|
}
|
|
|
|
// 其他
|
|
return time.toLocaleDateString('zh-CN') + ' ' + time.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
|
|
}
|
|
|
|
// 复制到剪贴板
|
|
copyToClipboard(text) {
|
|
return new Promise((resolve, reject) => {
|
|
wx.setClipboardData({
|
|
data: text,
|
|
success: () => {
|
|
this.showSuccess('已复制到剪贴板');
|
|
resolve();
|
|
},
|
|
fail: (error) => {
|
|
this.showError('复制失败');
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 震动反馈
|
|
vibrateShort() {
|
|
wx.vibrateShort({
|
|
type: 'light'
|
|
});
|
|
}
|
|
|
|
// 震动反馈(长)
|
|
vibrateLong() {
|
|
wx.vibrateLong();
|
|
}
|
|
}
|
|
|
|
// 创建全局单例
|
|
const uiHelper = new UIHelper();
|
|
|
|
module.exports = uiHelper;
|