524 lines
14 KiB
JavaScript
Raw Permalink 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.

// 环境配置
const environments = {
development: {
apiUrl: "https://test.hshuishang.com",
picUrl: "https://test.hshuishang.com",
aliyunOssUrl: "https://wechat-img-file.oss-cn-beijing.aliyuncs.com",
staticUrl: "https://static.hshuishang.com",
},
production: {
apiUrl: "https://api.hshuishang.com",
picUrl: "https://api.hshuishang.com",
aliyunOssUrl: "https://wechat-img-file.oss-cn-beijing.aliyuncs.com",
staticUrl: "https://static.hshuishang.com",
},
};
// 判断当前环境
const getCurrentEnvironment = () => {
// 1. 优先通过NODE_ENV判断
if (process && process.env && process.env.NODE_ENV) {
return process.env.NODE_ENV;
}
// 2. 微信小程序环境判断
if (typeof wx !== "undefined" && wx.getAccountInfoSync) {
try {
const accountInfo = wx.getAccountInfoSync();
const envVersion = accountInfo.miniProgram.envVersion;
// 根据微信小程序环境返回对应环境标识
if (envVersion === "release") {
return "production"; // 正式版
} else if (envVersion === "trial") {
return "development"; // 体验版
} else if (envVersion === "develop") {
return "development"; // 开发版
}
} catch (e) {
console.warn("获取小程序环境信息失败:", e);
}
}
// 3. 通过全局配置判断例如Vercel等平台的环境变量
if (typeof global !== "undefined" && global.env) {
return global.env;
}
// 4. 检查是否有全局的uni对象可能是uni-app环境
if (typeof uni !== "undefined") {
// 可以根据实际情况添加更多uni-app环境的判断逻辑
}
// 默认返回生产环境,避免线上环境使用开发地址
return "production";
};
// 获取当前环境配置
const currentEnv = getCurrentEnvironment();
console.log("🚀 ~ currentEnv:", currentEnv)
const envConfig = environments[currentEnv] || environments.production;
export const RequsetUrl = envConfig.apiUrl; // 请求地址前缀
export const picUrl = envConfig.picUrl; // 图片地址前缀
export const aliyunOssUrl = envConfig.aliyunOssUrl; // 阿里云OSS地址
export const staticUrl = envConfig.staticUrl; // 静态资源地址
/**
* 处理图片URL根据环境自动替换
* @param {string} url - 原始图片URL
* @returns {string} - 处理后的图片URL
*/
export const processImageUrl = (url) => {
if (!url) return url;
// 如果URL包含阿里云OSS地址则根据环境替换
if (url.includes(aliyunOssUrl)) {
if (currentEnv === "production") {
url = url.replace(aliyunOssUrl, staticUrl);
return url;
}
}
return url;
};
/**
* @description 小程序跳转方法二次封装
* @method NavgateTo
* @param {String} path - 跳转的目标页面路径
* @param {Object} options - 配置选项对象
* @param {Boolean} options.isLogin - 是否需要校验登录态,默认为 true
* @param {Boolean} options.forceReplace - 是否强制替换当前页面解决webview数量限制问题默认为 false
*/
export const NavgateTo = (path, options = {}) => {
// 首页不需要登录验证
if (path === "/pages/index/index") {
uni.navigateTo({ url: path });
return;
}
const { isLogin = true, forceReplace = false } = options;
const ctoken = uni.getStorageSync("ctoken");
// 登录校验
if (isLogin && !ctoken) {
uni.redirectTo({ url: "/pages/login/login" });
return;
}
// 返回上一页
if (path == "1") {
uni.navigateBack({
delta: 1,
});
return;
}
// 选择合适的跳转方式
const navigateOptions = {
url: path,
fail: (err) => {
// 处理webview数量限制错误
if (err.errMsg && err.errMsg.includes("webview count limit")) {
console.warn("检测到webview数量限制自动切换为redirectTo模式");
uni.redirectTo({ url: path });
} else {
console.error("页面跳转失败:", err);
}
},
};
// 如果强制替换使用redirectTo
if (forceReplace) {
uni.redirectTo(navigateOptions);
} else {
uni.navigateTo(navigateOptions);
}
};
/**
* 封装请求方法
* @param {string} url 请求地址
* @param {string} method 请求方法
* @param {Object} data 请求参数
* @param {Object} options 配置选项对象
* @param {Boolean} options.silent - 是否显示loading默认为 true
* @param {Boolean} options.nested - 是否平铺接口返回参数,默认为 false
* @returns {Promise} 返回一个Promise对象
*/
export const request = (url, method = "POST", data = {}, options = {}) => {
const { silent = true, nested = false } = options;
let ctoken = null;
if (options.token) {
ctoken = options.token;
} else {
ctoken = uni.getStorageSync("ctoken"); // 后续接口强依赖强校验该字段
}
if (silent) {
uni.showLoading({
title: "加载中",
mask: true,
});
}
let params = {
user_id: uni.getStorageSync("userId"),
...data,
};
return new Promise((resolve, reject) => {
uni.request({
url: RequsetUrl + url,
method: method,
data: params,
header: {
"Content-Type": "application/json",
Authorization: ctoken,
// ...header,
},
success: (res) => {
// console.log('请求成功,接口返参', res);
if (
res.statusCode == 401 ||
(res.statusCode == 500 && res.data.msg == "效验令牌失败")
) {
uni.removeStorageSync("ctoken");
uni.removeStorageSync("userId");
uni.removeStorageSync("is_worker");
uni.showModal({
title: "提示",
content: "请登录后查看",
confirmText: "去登陆",
complete: (res) => {
if (res.cancel) {
uni.hideLoading();
uni.redirectTo({
url: "/pages/index/index",
});
return;
}
if (res.confirm) {
uni.redirectTo({
url: "/pages/login/login",
});
}
},
});
return;
}
if (res.statusCode === 200) {
if (silent) {
uni.hideLoading();
}
if (options?.nested) {
let data = {
...res.data,
};
resolve(data); // 请求成功
return;
}
resolve(res.data.data); // 请求成功
} else {
console.log("走到这列");
uni.hideLoading();
uni.showToast({
title: res.data.msg || "请求失败",
icon: "none",
});
reject({
code: res.statusCode,
message: res.data.msg || "请求失败",
data: res.data,
});
}
},
fail: (err) => {
uni.hideLoading();
uni.showToast({
title: res.data.msg || "请求失败",
icon: "none",
});
reject({
code: -1,
message: "接口异常,请稍后重试",
error: err,
});
},
});
});
};
/**
* 校验手机号是否合法
* @param {string} option 请求方法
* @returns {Boolean} 手机号是否正确
*/
export const isPhone = (option) => {
if (option.length != 11) {
return false;
}
if (
!/^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/.test(option)
) {
return false;
} else {
return true;
}
};
/**
* 精确的浮点数运算
* @param {number} num1 - 第一个数字
* @param {number} num2 - 第二个数字
* @param {string} operator - 运算符,支持 '+', '-', '*', '/', '+='
* @returns {number} - 运算结果
*/
export const floatCalculate = (num1, num2, operator) => {
// 获取小数位数
function getPrecision(num) {
const str = num.toString();
const decimalIndex = str.indexOf(".");
return decimalIndex === -1 ? 0 : str.length - decimalIndex - 1;
}
// 计算放大倍数
const precision1 = getPrecision(num1);
const precision2 = getPrecision(num2);
const maxPrecision = Math.max(precision1, precision2);
const factor = Math.pow(10, maxPrecision);
// 将数字转换为整数
const intNum1 = Math.round(num1 * factor);
const intNum2 = Math.round(num2 * factor);
// 根据运算符进行计算
let result;
switch (operator) {
case "+":
result = (intNum1 + intNum2) / factor;
break;
case "-":
result = (intNum1 - intNum2) / factor;
break;
case "*":
result = (intNum1 * intNum2) / (factor * factor);
break;
case "/":
result = intNum1 / intNum2;
break;
case "+=":
result = (intNum1 + intNum2) / factor;
break;
default:
throw new Error("不支持的运算符");
}
return result;
};
/**
* 图片上传
* @param {string} filename - 图片上传地址
* @param {Function} fn - 接口回调函数
*/
export const upload = (filename, fn) => {
uni.showLoading({
title: "上传中",
mask: true,
});
uni.uploadFile({
url: RequsetUrl + "/api/v1/public/upload-image",
filePath: filename,
name: "image",
formData: {
uid: uni.getStorageSync("uid"),
},
success: (f) => {
uni.hideLoading();
fn(JSON.parse(f.data));
},
fail: (res) => {
uni.hideLoading();
console.log(res);
uni.showToast({
title: "上传文件失败",
icon: "none",
});
},
complete: () => {},
});
};
/**
* 视频上传
* @param {string} filename - 图片上传地址
* @param {Function} fn - 接口回调函数
*/
export const uploadVideo = (filename, fn) => {
uni.showLoading({
title: "上传中",
mask: true,
});
uni.uploadFile({
url: RequsetUrl + "/api/v1/public/upload-video",
filePath: filename,
name: "file",
formData: {
uid: uni.getStorageSync("uid"),
},
success: (f) => {
uni.hideLoading();
fn(JSON.parse(f.data));
},
fail: (res) => {
uni.hideLoading();
console.log(res);
uni.showToast({
title: "上传文件失败",
icon: "none",
});
},
complete: () => {},
});
};
/**
* 计算两地之间的距离
* @param {string} lat1 第一个点的纬度
* @param {string} lon1 第一个点的经度
* @param {string} lat2 第二个点的纬度
* @param {string} lon2 第二个点的经度
* @returns {number} 距离
*/
export const calculateDistance = (lat1, lon1, lat2, lon2) => {
// 将经纬度转换为弧度
const toRad = (value) => (value * Math.PI) / 180;
const R = 6371; // 地球半径(单位:千米)
const dLat = toRad(lat2 - lat1);
const dLon = toRad(lon2 - lon1);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRad(lat1)) *
Math.cos(toRad(lat2)) *
Math.sin(dLon / 2) *
Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c; // 最终距离(单位:千米)
return distance;
};
//
export const menuButtonInfo = () => {
const systemInfo = uni.getSystemInfoSync();
const platform = systemInfo.platform;
if (platform === "ios") {
// TODO: ios待测试
return {
height: systemInfo.statusBarHeight,
top: 44,
};
}
if (platform === "android") {
return {
height: systemInfo.statusBarHeight,
top: 44, // 自定义导航栏默认高度
};
}
if (systemInfo.uniPlatform === "mp-weixin") {
// 微信小程序、支付宝小程序等,平台标识以 'mp-' 开头
return uni.getMenuButtonBoundingClientRect();
} else {
return "Unknown";
}
};
/**
* 获取服务供应商
* @param {string} opt - 服务类型
* @returns {Promise} 返回一个Promise对象
*/
export const getProviderPromise = (opt) => {
return new Promise((resolve, reject) => {
uni.getProvider({
service: opt,
success: (res) => {
console.log("获取支付服务提供商成功:", res.provider);
resolve(res.provider);
},
fail: (err) => {
console.error("获取支付服务提供商失败:", err);
reject(err);
},
});
});
};
//数组去重
export const uniqueByField = (arr, field) => {
const seen = {};
return arr.filter((item) => {
const key = item[field];
return seen.hasOwnProperty(key) ? false : (seen[key] = true);
});
};
// 校验邮箱地址是否合法
export const validateEmail = (email) => {
// 正则表达式验证邮箱格式
const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!regex.test(email)) {
return false;
}
return true;
};
/**
* 防抖函数
* @param {Function} fn 需要防抖的函数
* @param {number} delay 延迟时间(毫秒)
* @param {boolean} immediate 是否立即执行
* @return {Function} 返回防抖处理后的函数
*/
export const debounce = (fn, delay = 300, immediate = false) => {
let timer = null;
return function (...args) {
const context = this;
if (timer) clearTimeout(timer);
if (immediate && !timer) {
fn.apply(context, args);
}
timer = setTimeout(() => {
if (!immediate) {
fn.apply(context, args);
}
timer = null;
}, delay);
};
};
/**
* 格式化日期
* @param {date} 时间戳
*/
export const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};