feat :init
This commit is contained in:
489
miniprogram/utils/api.js
Normal file
489
miniprogram/utils/api.js
Normal file
@@ -0,0 +1,489 @@
|
||||
/**
|
||||
* API服务工具
|
||||
* 统一处理网络请求、错误处理、token管理等
|
||||
*/
|
||||
|
||||
const app = getApp();
|
||||
|
||||
// API配置
|
||||
const API_CONFIG = {
|
||||
baseUrl: 'https://api.xiaozhi.com', // 生产环境API地址
|
||||
timeout: 10000,
|
||||
retryCount: 3,
|
||||
retryDelay: 1000
|
||||
};
|
||||
|
||||
// 请求状态码
|
||||
const STATUS_CODE = {
|
||||
SUCCESS: 200,
|
||||
UNAUTHORIZED: 401,
|
||||
FORBIDDEN: 403,
|
||||
NOT_FOUND: 404,
|
||||
SERVER_ERROR: 500,
|
||||
NETWORK_ERROR: -1
|
||||
};
|
||||
|
||||
// 错误消息映射
|
||||
const ERROR_MESSAGES = {
|
||||
[STATUS_CODE.UNAUTHORIZED]: '登录已过期,请重新登录',
|
||||
[STATUS_CODE.FORBIDDEN]: '没有访问权限',
|
||||
[STATUS_CODE.NOT_FOUND]: '请求的资源不存在',
|
||||
[STATUS_CODE.SERVER_ERROR]: '服务器内部错误',
|
||||
[STATUS_CODE.NETWORK_ERROR]: '网络连接失败,请检查网络设置'
|
||||
};
|
||||
|
||||
/**
|
||||
* 基础请求方法
|
||||
* @param {Object} options 请求配置
|
||||
* @returns {Promise} 请求结果
|
||||
*/
|
||||
function request(options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const {
|
||||
url,
|
||||
method = 'GET',
|
||||
data = {},
|
||||
header = {},
|
||||
needAuth = true,
|
||||
showLoading = true,
|
||||
loadingText = '加载中...',
|
||||
retryCount = 0
|
||||
} = options;
|
||||
|
||||
// 显示加载提示
|
||||
if (showLoading) {
|
||||
wx.showLoading({
|
||||
title: loadingText,
|
||||
mask: true
|
||||
});
|
||||
}
|
||||
|
||||
// 构建请求头
|
||||
const requestHeader = {
|
||||
'Content-Type': 'application/json',
|
||||
...header
|
||||
};
|
||||
|
||||
// 添加认证token
|
||||
if (needAuth && app.globalData.token) {
|
||||
requestHeader['Authorization'] = `Bearer ${app.globalData.token}`;
|
||||
}
|
||||
|
||||
// 发起请求
|
||||
wx.request({
|
||||
url: `${API_CONFIG.baseUrl}${url}`,
|
||||
method,
|
||||
data,
|
||||
header: requestHeader,
|
||||
timeout: API_CONFIG.timeout,
|
||||
success: (res) => {
|
||||
if (showLoading) {
|
||||
wx.hideLoading();
|
||||
}
|
||||
|
||||
const { statusCode, data: responseData } = res;
|
||||
|
||||
// 请求成功
|
||||
if (statusCode === STATUS_CODE.SUCCESS) {
|
||||
if (responseData.code === 0) {
|
||||
resolve(responseData);
|
||||
} else {
|
||||
// 业务错误
|
||||
handleBusinessError(responseData, reject);
|
||||
}
|
||||
} else {
|
||||
// HTTP错误
|
||||
handleHttpError(statusCode, reject, options, retryCount);
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
if (showLoading) {
|
||||
wx.hideLoading();
|
||||
}
|
||||
|
||||
console.error('Request failed:', error);
|
||||
|
||||
// 网络错误重试
|
||||
if (retryCount < API_CONFIG.retryCount) {
|
||||
setTimeout(() => {
|
||||
request({
|
||||
...options,
|
||||
retryCount: retryCount + 1
|
||||
}).then(resolve).catch(reject);
|
||||
}, API_CONFIG.retryDelay);
|
||||
} else {
|
||||
handleNetworkError(error, reject);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理业务错误
|
||||
* @param {Object} responseData 响应数据
|
||||
* @param {Function} reject 拒绝函数
|
||||
*/
|
||||
function handleBusinessError(responseData, reject) {
|
||||
const { code, message } = responseData;
|
||||
|
||||
// 特殊错误码处理
|
||||
switch (code) {
|
||||
case 401:
|
||||
// token过期,清除登录状态
|
||||
app.clearLoginStatus();
|
||||
wx.showToast({
|
||||
title: '登录已过期',
|
||||
icon: 'none'
|
||||
});
|
||||
// 跳转到登录页
|
||||
wx.navigateTo({
|
||||
url: '/pages/login/login'
|
||||
});
|
||||
break;
|
||||
case 403:
|
||||
wx.showToast({
|
||||
title: '权限不足',
|
||||
icon: 'none'
|
||||
});
|
||||
break;
|
||||
default:
|
||||
wx.showToast({
|
||||
title: message || '操作失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
|
||||
reject({
|
||||
code,
|
||||
message,
|
||||
type: 'business'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理HTTP错误
|
||||
* @param {Number} statusCode 状态码
|
||||
* @param {Function} reject 拒绝函数
|
||||
* @param {Object} options 请求选项
|
||||
* @param {Number} retryCount 重试次数
|
||||
*/
|
||||
function handleHttpError(statusCode, reject, options, retryCount) {
|
||||
const message = ERROR_MESSAGES[statusCode] || '请求失败';
|
||||
|
||||
// 401错误特殊处理
|
||||
if (statusCode === STATUS_CODE.UNAUTHORIZED) {
|
||||
app.clearLoginStatus();
|
||||
wx.navigateTo({
|
||||
url: '/pages/login/login'
|
||||
});
|
||||
}
|
||||
|
||||
// 5xx错误重试
|
||||
if (statusCode >= 500 && retryCount < API_CONFIG.retryCount) {
|
||||
setTimeout(() => {
|
||||
request({
|
||||
...options,
|
||||
retryCount: retryCount + 1
|
||||
}).then(resolve).catch(reject);
|
||||
}, API_CONFIG.retryDelay);
|
||||
return;
|
||||
}
|
||||
|
||||
wx.showToast({
|
||||
title: message,
|
||||
icon: 'none'
|
||||
});
|
||||
|
||||
reject({
|
||||
code: statusCode,
|
||||
message,
|
||||
type: 'http'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理网络错误
|
||||
* @param {Object} error 错误对象
|
||||
* @param {Function} reject 拒绝函数
|
||||
*/
|
||||
function handleNetworkError(error, reject) {
|
||||
wx.showToast({
|
||||
title: '网络连接失败',
|
||||
icon: 'none'
|
||||
});
|
||||
|
||||
reject({
|
||||
code: STATUS_CODE.NETWORK_ERROR,
|
||||
message: '网络连接失败',
|
||||
type: 'network',
|
||||
error
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* GET请求
|
||||
* @param {String} url 请求地址
|
||||
* @param {Object} params 请求参数
|
||||
* @param {Object} options 其他选项
|
||||
* @returns {Promise} 请求结果
|
||||
*/
|
||||
function get(url, params = {}, options = {}) {
|
||||
const queryString = Object.keys(params)
|
||||
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
||||
.join('&');
|
||||
|
||||
const requestUrl = queryString ? `${url}?${queryString}` : url;
|
||||
|
||||
return request({
|
||||
url: requestUrl,
|
||||
method: 'GET',
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* POST请求
|
||||
* @param {String} url 请求地址
|
||||
* @param {Object} data 请求数据
|
||||
* @param {Object} options 其他选项
|
||||
* @returns {Promise} 请求结果
|
||||
*/
|
||||
function post(url, data = {}, options = {}) {
|
||||
return request({
|
||||
url,
|
||||
method: 'POST',
|
||||
data,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT请求
|
||||
* @param {String} url 请求地址
|
||||
* @param {Object} data 请求数据
|
||||
* @param {Object} options 其他选项
|
||||
* @returns {Promise} 请求结果
|
||||
*/
|
||||
function put(url, data = {}, options = {}) {
|
||||
return request({
|
||||
url,
|
||||
method: 'PUT',
|
||||
data,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE请求
|
||||
* @param {String} url 请求地址
|
||||
* @param {Object} options 其他选项
|
||||
* @returns {Promise} 请求结果
|
||||
*/
|
||||
function del(url, options = {}) {
|
||||
return request({
|
||||
url,
|
||||
method: 'DELETE',
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
* @param {String} url 上传地址
|
||||
* @param {String} filePath 文件路径
|
||||
* @param {Object} formData 表单数据
|
||||
* @param {Object} options 其他选项
|
||||
* @returns {Promise} 上传结果
|
||||
*/
|
||||
function upload(url, filePath, formData = {}, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const {
|
||||
name = 'file',
|
||||
header = {},
|
||||
needAuth = true,
|
||||
showLoading = true
|
||||
} = options;
|
||||
|
||||
if (showLoading) {
|
||||
wx.showLoading({
|
||||
title: '上传中...',
|
||||
mask: true
|
||||
});
|
||||
}
|
||||
|
||||
// 构建请求头
|
||||
const requestHeader = { ...header };
|
||||
if (needAuth && app.globalData.token) {
|
||||
requestHeader['Authorization'] = `Bearer ${app.globalData.token}`;
|
||||
}
|
||||
|
||||
wx.uploadFile({
|
||||
url: `${API_CONFIG.baseUrl}${url}`,
|
||||
filePath,
|
||||
name,
|
||||
formData,
|
||||
header: requestHeader,
|
||||
success: (res) => {
|
||||
if (showLoading) {
|
||||
wx.hideLoading();
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(res.data);
|
||||
if (data.code === 0) {
|
||||
resolve(data);
|
||||
} else {
|
||||
handleBusinessError(data, reject);
|
||||
}
|
||||
} catch (error) {
|
||||
reject({
|
||||
code: -1,
|
||||
message: '响应数据解析失败',
|
||||
type: 'parse',
|
||||
error
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
if (showLoading) {
|
||||
wx.hideLoading();
|
||||
}
|
||||
handleNetworkError(error, reject);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 会员相关API
|
||||
const memberAPI = {
|
||||
// 获取用户信息
|
||||
getUserInfo() {
|
||||
return get('/api/member/user/info');
|
||||
},
|
||||
|
||||
// 微信登录
|
||||
wxLogin(code) {
|
||||
return post('/api/member/auth/wx-login', { code }, { needAuth: false });
|
||||
},
|
||||
|
||||
// 获取会员信息
|
||||
getMemberInfo() {
|
||||
return get('/api/member/info');
|
||||
},
|
||||
|
||||
// 绑定会员
|
||||
bindMember(memberData) {
|
||||
return post('/api/member/bind', memberData);
|
||||
},
|
||||
|
||||
// 解绑会员
|
||||
unbindMember() {
|
||||
return post('/api/member/unbind');
|
||||
},
|
||||
|
||||
// 获取会员权益
|
||||
getMemberBenefits() {
|
||||
return get('/api/member/benefits');
|
||||
},
|
||||
|
||||
// 验证会员权益
|
||||
verifyBenefit(benefitType) {
|
||||
return post('/api/member/verify-benefit', { benefitType });
|
||||
},
|
||||
|
||||
// 获取会员等级配置
|
||||
getMemberLevels() {
|
||||
return get('/api/member/levels');
|
||||
},
|
||||
|
||||
// 升级会员
|
||||
upgradeMember(targetLevel) {
|
||||
return post('/api/member/upgrade', { targetLevel });
|
||||
},
|
||||
|
||||
// 获取绑定历史
|
||||
getBindHistory(page = 1, size = 10) {
|
||||
return get('/api/member/bind-history', { page, size });
|
||||
},
|
||||
|
||||
// 获取权益使用记录
|
||||
getBenefitUsage(page = 1, size = 10) {
|
||||
return get('/api/member/benefit-usage', { page, size });
|
||||
},
|
||||
|
||||
// 邀请好友
|
||||
inviteFriend(inviteCode) {
|
||||
return post('/api/member/invite', { inviteCode });
|
||||
},
|
||||
|
||||
// 获取邀请记录
|
||||
getInviteHistory(page = 1, size = 10) {
|
||||
return get('/api/member/invite-history', { page, size });
|
||||
}
|
||||
};
|
||||
|
||||
// 支付相关API
|
||||
const paymentAPI = {
|
||||
// 创建支付订单
|
||||
createOrder(orderData) {
|
||||
return post('/api/payment/create-order', orderData);
|
||||
},
|
||||
|
||||
// 查询订单状态
|
||||
queryOrder(orderId) {
|
||||
return get(`/api/payment/order/${orderId}`);
|
||||
},
|
||||
|
||||
// 取消订单
|
||||
cancelOrder(orderId) {
|
||||
return post(`/api/payment/order/${orderId}/cancel`);
|
||||
},
|
||||
|
||||
// 申请退款
|
||||
requestRefund(orderId, refundData) {
|
||||
return post(`/api/payment/order/${orderId}/refund`, refundData);
|
||||
},
|
||||
|
||||
// 查询退款状态
|
||||
queryRefund(refundId) {
|
||||
return get(`/api/payment/refund/${refundId}`);
|
||||
}
|
||||
};
|
||||
|
||||
// 通用API
|
||||
const commonAPI = {
|
||||
// 获取系统配置
|
||||
getSystemConfig() {
|
||||
return get('/api/common/config', {}, { needAuth: false });
|
||||
},
|
||||
|
||||
// 上传图片
|
||||
uploadImage(filePath) {
|
||||
return upload('/api/common/upload/image', filePath);
|
||||
},
|
||||
|
||||
// 发送验证码
|
||||
sendVerifyCode(phone) {
|
||||
return post('/api/common/send-verify-code', { phone }, { needAuth: false });
|
||||
},
|
||||
|
||||
// 验证验证码
|
||||
verifyCode(phone, code) {
|
||||
return post('/api/common/verify-code', { phone, code }, { needAuth: false });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
request,
|
||||
get,
|
||||
post,
|
||||
put,
|
||||
del: del,
|
||||
upload,
|
||||
memberAPI,
|
||||
paymentAPI,
|
||||
commonAPI,
|
||||
API_CONFIG,
|
||||
STATUS_CODE
|
||||
};
|
||||
543
miniprogram/utils/util.js
Normal file
543
miniprogram/utils/util.js
Normal file
@@ -0,0 +1,543 @@
|
||||
/**
|
||||
* 通用工具函数
|
||||
*/
|
||||
|
||||
/**
|
||||
* 格式化时间
|
||||
* @param {Date|String|Number} date 日期
|
||||
* @param {String} format 格式化字符串
|
||||
* @returns {String} 格式化后的时间字符串
|
||||
*/
|
||||
function formatTime(date, format = 'YYYY-MM-DD HH:mm:ss') {
|
||||
if (!date) return '';
|
||||
|
||||
const d = new Date(date);
|
||||
if (isNaN(d.getTime())) return '';
|
||||
|
||||
const year = d.getFullYear();
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(d.getDate()).padStart(2, '0');
|
||||
const hour = String(d.getHours()).padStart(2, '0');
|
||||
const minute = String(d.getMinutes()).padStart(2, '0');
|
||||
const second = String(d.getSeconds()).padStart(2, '0');
|
||||
|
||||
return format
|
||||
.replace('YYYY', year)
|
||||
.replace('MM', month)
|
||||
.replace('DD', day)
|
||||
.replace('HH', hour)
|
||||
.replace('mm', minute)
|
||||
.replace('ss', second);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化相对时间
|
||||
* @param {Date|String|Number} date 日期
|
||||
* @returns {String} 相对时间字符串
|
||||
*/
|
||||
function formatRelativeTime(date) {
|
||||
if (!date) return '';
|
||||
|
||||
const d = new Date(date);
|
||||
if (isNaN(d.getTime())) return '';
|
||||
|
||||
const now = new Date();
|
||||
const diff = now.getTime() - d.getTime();
|
||||
const seconds = Math.floor(diff / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
|
||||
if (seconds < 60) {
|
||||
return '刚刚';
|
||||
} else if (minutes < 60) {
|
||||
return `${minutes}分钟前`;
|
||||
} else if (hours < 24) {
|
||||
return `${hours}小时前`;
|
||||
} else if (days < 7) {
|
||||
return `${days}天前`;
|
||||
} else {
|
||||
return formatTime(date, 'MM-DD');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 防抖函数
|
||||
* @param {Function} func 要防抖的函数
|
||||
* @param {Number} delay 延迟时间
|
||||
* @returns {Function} 防抖后的函数
|
||||
*/
|
||||
function debounce(func, delay = 300) {
|
||||
let timer = null;
|
||||
return function (...args) {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
func.apply(this, args);
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 节流函数
|
||||
* @param {Function} func 要节流的函数
|
||||
* @param {Number} delay 延迟时间
|
||||
* @returns {Function} 节流后的函数
|
||||
*/
|
||||
function throttle(func, delay = 300) {
|
||||
let timer = null;
|
||||
return function (...args) {
|
||||
if (!timer) {
|
||||
timer = setTimeout(() => {
|
||||
func.apply(this, args);
|
||||
timer = null;
|
||||
}, delay);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 深拷贝
|
||||
* @param {Any} obj 要拷贝的对象
|
||||
* @returns {Any} 拷贝后的对象
|
||||
*/
|
||||
function deepClone(obj) {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime());
|
||||
}
|
||||
|
||||
if (obj instanceof Array) {
|
||||
return obj.map(item => deepClone(item));
|
||||
}
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
const cloned = {};
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
cloned[key] = deepClone(obj[key]);
|
||||
}
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一ID
|
||||
* @param {String} prefix 前缀
|
||||
* @returns {String} 唯一ID
|
||||
*/
|
||||
function generateId(prefix = '') {
|
||||
const timestamp = Date.now().toString(36);
|
||||
const random = Math.random().toString(36).substr(2, 5);
|
||||
return `${prefix}${timestamp}${random}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证手机号
|
||||
* @param {String} phone 手机号
|
||||
* @returns {Boolean} 是否有效
|
||||
*/
|
||||
function validatePhone(phone) {
|
||||
const phoneRegex = /^1[3-9]\d{9}$/;
|
||||
return phoneRegex.test(phone);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证邮箱
|
||||
* @param {String} email 邮箱
|
||||
* @returns {Boolean} 是否有效
|
||||
*/
|
||||
function validateEmail(email) {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证身份证号
|
||||
* @param {String} idCard 身份证号
|
||||
* @returns {Boolean} 是否有效
|
||||
*/
|
||||
function validateIdCard(idCard) {
|
||||
const idCardRegex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
|
||||
return idCardRegex.test(idCard);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化金额
|
||||
* @param {Number} amount 金额
|
||||
* @param {Number} decimals 小数位数
|
||||
* @returns {String} 格式化后的金额
|
||||
*/
|
||||
function formatAmount(amount, decimals = 2) {
|
||||
if (isNaN(amount)) return '0.00';
|
||||
return Number(amount).toFixed(decimals);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小
|
||||
* @param {Number} bytes 字节数
|
||||
* @returns {String} 格式化后的文件大小
|
||||
*/
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes === 0) return '0 B';
|
||||
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取URL参数
|
||||
* @param {String} url URL地址
|
||||
* @returns {Object} 参数对象
|
||||
*/
|
||||
function getUrlParams(url) {
|
||||
const params = {};
|
||||
const urlObj = new URL(url);
|
||||
|
||||
for (const [key, value] of urlObj.searchParams) {
|
||||
params[key] = value;
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL参数
|
||||
* @param {Object} params 参数对象
|
||||
* @returns {String} 参数字符串
|
||||
*/
|
||||
function buildUrlParams(params) {
|
||||
return Object.keys(params)
|
||||
.filter(key => params[key] !== undefined && params[key] !== null)
|
||||
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
||||
.join('&');
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储数据到本地
|
||||
* @param {String} key 键名
|
||||
* @param {Any} data 数据
|
||||
* @param {Number} expire 过期时间(毫秒)
|
||||
*/
|
||||
function setStorage(key, data, expire = 0) {
|
||||
const item = {
|
||||
data,
|
||||
timestamp: Date.now(),
|
||||
expire
|
||||
};
|
||||
|
||||
try {
|
||||
wx.setStorageSync(key, JSON.stringify(item));
|
||||
} catch (error) {
|
||||
console.error('Storage set error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从本地获取数据
|
||||
* @param {String} key 键名
|
||||
* @returns {Any} 数据
|
||||
*/
|
||||
function getStorage(key) {
|
||||
try {
|
||||
const item = wx.getStorageSync(key);
|
||||
if (!item) return null;
|
||||
|
||||
const parsed = JSON.parse(item);
|
||||
const { data, timestamp, expire } = parsed;
|
||||
|
||||
// 检查是否过期
|
||||
if (expire > 0 && Date.now() - timestamp > expire) {
|
||||
wx.removeStorageSync(key);
|
||||
return null;
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Storage get error:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除本地存储数据
|
||||
* @param {String} key 键名
|
||||
*/
|
||||
function removeStorage(key) {
|
||||
try {
|
||||
wx.removeStorageSync(key);
|
||||
} catch (error) {
|
||||
console.error('Storage remove error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空本地存储
|
||||
*/
|
||||
function clearStorage() {
|
||||
try {
|
||||
wx.clearStorageSync();
|
||||
} catch (error) {
|
||||
console.error('Storage clear error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示成功提示
|
||||
* @param {String} title 提示内容
|
||||
* @param {Number} duration 显示时长
|
||||
*/
|
||||
function showSuccess(title, duration = 2000) {
|
||||
wx.showToast({
|
||||
title,
|
||||
icon: 'success',
|
||||
duration
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示错误提示
|
||||
* @param {String} title 提示内容
|
||||
* @param {Number} duration 显示时长
|
||||
*/
|
||||
function showError(title, duration = 2000) {
|
||||
wx.showToast({
|
||||
title,
|
||||
icon: 'error',
|
||||
duration
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示普通提示
|
||||
* @param {String} title 提示内容
|
||||
* @param {Number} duration 显示时长
|
||||
*/
|
||||
function showToast(title, duration = 2000) {
|
||||
wx.showToast({
|
||||
title,
|
||||
icon: 'none',
|
||||
duration
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示加载提示
|
||||
* @param {String} title 提示内容
|
||||
*/
|
||||
function showLoading(title = '加载中...') {
|
||||
wx.showLoading({
|
||||
title,
|
||||
mask: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏加载提示
|
||||
*/
|
||||
function hideLoading() {
|
||||
wx.hideLoading();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示确认对话框
|
||||
* @param {String} content 内容
|
||||
* @param {String} title 标题
|
||||
* @returns {Promise<Boolean>} 用户选择结果
|
||||
*/
|
||||
function showConfirm(content, title = '提示') {
|
||||
return new Promise((resolve) => {
|
||||
wx.showModal({
|
||||
title,
|
||||
content,
|
||||
success: (res) => {
|
||||
resolve(res.confirm);
|
||||
},
|
||||
fail: () => {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统信息
|
||||
* @returns {Object} 系统信息
|
||||
*/
|
||||
function getSystemInfo() {
|
||||
try {
|
||||
return wx.getSystemInfoSync();
|
||||
} catch (error) {
|
||||
console.error('Get system info error:', error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查网络状态
|
||||
* @returns {Promise<Object>} 网络状态
|
||||
*/
|
||||
function getNetworkType() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.getNetworkType({
|
||||
success: resolve,
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制到剪贴板
|
||||
* @param {String} data 要复制的内容
|
||||
* @returns {Promise<Boolean>} 复制结果
|
||||
*/
|
||||
function copyToClipboard(data) {
|
||||
return new Promise((resolve) => {
|
||||
wx.setClipboardData({
|
||||
data,
|
||||
success: () => {
|
||||
showSuccess('复制成功');
|
||||
resolve(true);
|
||||
},
|
||||
fail: () => {
|
||||
showError('复制失败');
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览图片
|
||||
* @param {String} current 当前图片URL
|
||||
* @param {Array} urls 图片URL列表
|
||||
*/
|
||||
function previewImage(current, urls = []) {
|
||||
wx.previewImage({
|
||||
current,
|
||||
urls: urls.length > 0 ? urls : [current]
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 拨打电话
|
||||
* @param {String} phoneNumber 电话号码
|
||||
*/
|
||||
function makePhoneCall(phoneNumber) {
|
||||
wx.makePhoneCall({
|
||||
phoneNumber,
|
||||
fail: () => {
|
||||
showError('拨打电话失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员等级文本
|
||||
* @param {String} level 会员等级
|
||||
* @returns {String} 等级文本
|
||||
*/
|
||||
function getMemberLevelText(level) {
|
||||
const levelMap = {
|
||||
'free': '普通用户',
|
||||
'vip': 'VIP会员',
|
||||
'svip': 'SVIP会员'
|
||||
};
|
||||
return levelMap[level] || '未知等级';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员等级颜色
|
||||
* @param {String} level 会员等级
|
||||
* @returns {String} 颜色值
|
||||
*/
|
||||
function getMemberLevelColor(level) {
|
||||
const colorMap = {
|
||||
'free': '#6C757D',
|
||||
'vip': '#FFD700',
|
||||
'svip': '#FF6B35'
|
||||
};
|
||||
return colorMap[level] || '#6C757D';
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算会员到期时间
|
||||
* @param {String} expireTime 到期时间
|
||||
* @returns {Object} 时间信息
|
||||
*/
|
||||
function calculateMemberExpire(expireTime) {
|
||||
if (!expireTime) {
|
||||
return {
|
||||
isExpired: true,
|
||||
daysLeft: 0,
|
||||
text: '已过期'
|
||||
};
|
||||
}
|
||||
|
||||
const expire = new Date(expireTime);
|
||||
const now = new Date();
|
||||
const diff = expire.getTime() - now.getTime();
|
||||
const daysLeft = Math.ceil(diff / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (daysLeft <= 0) {
|
||||
return {
|
||||
isExpired: true,
|
||||
daysLeft: 0,
|
||||
text: '已过期'
|
||||
};
|
||||
} else if (daysLeft <= 7) {
|
||||
return {
|
||||
isExpired: false,
|
||||
daysLeft,
|
||||
text: `${daysLeft}天后到期`
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
isExpired: false,
|
||||
daysLeft,
|
||||
text: formatTime(expireTime, 'YYYY-MM-DD') + ' 到期'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
formatTime,
|
||||
formatRelativeTime,
|
||||
debounce,
|
||||
throttle,
|
||||
deepClone,
|
||||
generateId,
|
||||
validatePhone,
|
||||
validateEmail,
|
||||
validateIdCard,
|
||||
formatAmount,
|
||||
formatFileSize,
|
||||
getUrlParams,
|
||||
buildUrlParams,
|
||||
setStorage,
|
||||
getStorage,
|
||||
removeStorage,
|
||||
clearStorage,
|
||||
showSuccess,
|
||||
showError,
|
||||
showToast,
|
||||
showLoading,
|
||||
hideLoading,
|
||||
showConfirm,
|
||||
getSystemInfo,
|
||||
getNetworkType,
|
||||
copyToClipboard,
|
||||
previewImage,
|
||||
makePhoneCall,
|
||||
getMemberLevelText,
|
||||
getMemberLevelColor,
|
||||
calculateMemberExpire
|
||||
};
|
||||
Reference in New Issue
Block a user