废话不多说,直接上代码。
util.js源码:
ini
// 查看单个图或 PDF 文件
export const previewSingleFile = (url) => {
const fileExtension = url.split(".").pop().toLowerCase(); // 获取文件扩展名
if (fileExtension === "pdf") {
// 如果是 PDF 文件
wx.downloadFile({
url: url, // 下载 PDF 文件
success: function (res) {
const filePath = res.tempFilePath; // 临时文件路径
wx.openDocument({
filePath: filePath, // 打开 PDF 文件
fileType: "pdf",
success: function () {
console.log("预览 PDF 成功");
},
fail: function () {
console.log("预览 PDF 失败");
},
});
},
fail: function () {
console.log("下载 PDF 失败");
},
});
} else {
// 如果是图片
wx.previewImage({
urls: [url],
success: function () {
console.log("预览图片成功");
},
fail: function () {
console.log("预览图片失败");
},
});
}
};
// 获取嵌套属性值的工具函数
const getNestedValue2 = (obj, path) => {
return path.split(".").reduce((acc, key) => acc && acc[key], obj);
};
// 设置嵌套属性值的工具函数
const setNestedValue2 = (obj, path, value) => {
const keys = path.split(".");
const lastKey = keys.pop();
const target = keys.reduce((acc, key) => (acc[key] = acc[key] || {}), obj);
target[lastKey] = value;
};
export function setPrePageData(dataObject, addType = "update", refresh = false) {
const pages = getCurrentPages(); // 获取页面栈
if (pages.length < 2) {
console.error("没有上一个页面,无法设置数据");
return;
}
const prevPage = pages[pages.length - 2]; // 获取上一个页面
console.log("addType", addType);
if (addType == "update") {
prevPage.setData(dataObject); // 动态设置多个键值对
if (prevPage && typeof prevPage.refresh === 'function' && refresh == true) {
prevPage.refresh();
}
} else if (addType == "add") {
// 拼接数据到上一个页面的数据数组第一个
const keyName = Object.keys(dataObject)[0];
const currentData = getNestedValue2(prevPage.data, keyName) || [];
const newData = [dataObject[keyName]];
// 使用工具函数设置嵌套数据
const updatedData = [...newData, ...currentData];
setNestedValue2(prevPage.data, keyName, updatedData);
if (prevPage && typeof prevPage.refresh === 'function' && refresh == true) {
prevPage.refresh();
}
// 更新页面数据
prevPage.setData({
[keyName]: updatedData,
});
}
wx.navigateBack(); // 返回上一个页面
}
//查看大图
export const previewImage = (url) => {
wx.previewImage({
urls: [url],
success: function () {
console.log("预览图片成功");
},
fail: function () {
console.log("预览图片失败");
},
});
};
//弹出底部弹窗 复制号码 拨打电话
export const showPhonePopup = (phoneNumber) => {
wx.showActionSheet({
itemList: ["复制号码", "拨打电话"],
success: function (res) {
if (res.tapIndex === 0) {
// 复制号码
wx.setClipboardData({
data: phoneNumber,
success: function () {
wx.showToast({
title: "号码已复制",
icon: "success",
});
},
fail: function () {
wx.showToast({
title: "复制失败",
icon: "none",
});
},
});
} else if (res.tapIndex === 1) {
// 拨打电话
wx.makePhoneCall({
phoneNumber: phoneNumber,
success: function () {
console.log("拨打电话成功");
},
fail: function () {
console.log("拨打电话失败");
},
});
}
},
fail: function (err) {
console.error("显示操作菜单失败", err);
},
});
};
//联系客服
export const callHelpPhone = () => {
wx.makePhoneCall({
phoneNumber: "4001168856 ", // 这里替换为你的客服电话
success: function () {
console.log("拨打电话成功");
},
fail: function () {
console.log("拨打电话失败");
},
});
};
/**
* 千分位
* @param {*} value
* @param {*} precision 保留几位小数
* @param {*} separator 分隔符
*/
export const thousands = (value, precision, separator = ",") => {
if (value == undefined || value == null) {
let num = precision ? "0." : "0";
for (let i = 0; i < Number(precision); i++) num += "0";
return num;
} else {
let [num, parts] = [value, []];
// 判断是否为数字
if (!isNaN(parseFloat(num)) && isFinite(num)) {
num = Number(num);
// 修复四舍五入逻辑
num = (
typeof precision !== "undefined"
? (
Math.round(num * Math.pow(10, precision)) /
Math.pow(10, precision)
).toFixed(precision)
: num
).toString();
parts = num.split(".");
parts[0] = parts[0]
.toString()
.replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1" + separator);
return parts.join(".");
}
return value;
}
};
// 定义一个模块作用域的变量
let isNavigating = false; // 在模块加载时初始化为 false
/**
* 公共跳转方法,带节流功能
* @param {string} url - 跳转的目标页面路径
* @param {object} options - 跳转参数(可选)
*/
export const navigateTo = (url, options = {}) => {
if (isNavigating) {
console.log("跳转过于频繁,请稍后再试");
return;
}
isNavigating = true; // 设置跳转标记
setTimeout(() => {
isNavigating = false; // 一段时间后解除跳转限制
}, 1000); // 节流时间,单位为毫秒
// 执行跳转
wx.navigateTo({
url,
...options,
success: () => {
console.log("跳转成功");
},
fail: (err) => {
console.error("跳转失败", err);
},
});
};
/**
* 公共跳转方法,带节流功能
* @param {string} url - 跳转的目标页面路径
* @param {object} options - 跳转参数(可选)
*/
export const redirectTo = (url, options = {}) => {
if (isNavigating) {
console.log("跳转过于频繁,请稍后再试");
return;
}
isNavigating = true; // 设置跳转标记
setTimeout(() => {
isNavigating = false; // 一段时间后解除跳转限制
}, 1000); // 节流时间,单位为毫秒
// 执行跳转
wx.redirectTo({
url,
...options,
success: () => {
console.log("跳转成功");
},
fail: (err) => {
console.error("跳转失败", err);
},
});
};
export const logout = () => {
wx.removeStorageSync("token");
const app = getApp();
app.globalData.token = null;
wx.reLaunch({
url: "/pages/splash/index",
});
};
export const isLogin = (jump = false) => {
const token = wx.getStorageSync("token");
if (!token) {
if (jump) {
wx.navigateTo({
url: "/pages/login/login",
});
}
return false;
}
return true;
};
export const formatTime = (date, type, nosecond) => {
if (!date) {
return "";
}
date = new Date(date);
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const hour = date.getHours();
const minute = date.getMinutes();
const second = date.getSeconds();
if (type) {
if (nosecond) {
return `${[year, month, day].map(formatNumber).join("-")} ${[hour, minute]
.map(formatNumber)
.join(":")}`;
} else {
return `${[year, month, day].map(formatNumber).join("-")} ${[
hour,
minute,
second,
]
.map(formatNumber)
.join(":")}`;
}
}
return [year, month, day].map(formatNumber).join("-");
};
export const formatNumber = (n) => {
n = n.toString();
return n[1] ? n : `0${n}`;
};
export function debounce(func, delay = 300) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
export function throttle(fn, interval) {
var enterTime = 0;
var gapTime = interval || 1500;
return function () {
var context = this;
var args = arguments;
var backTime = new Date();
if (backTime - enterTime > gapTime) {
fn.apply(context, args);
enterTime = backTime;
}
};
}
export function secKillClock(that, param, list) {
return setInterval(function () {
let allChange = false;
for (let idx = 0; idx < list.length; idx++) {
if (list[idx].seckillType == "2") {
allChange = true;
list[idx].timeLeft -= 1;
list[idx].timeLeftShow = secToClock(list[idx].timeLeft);
if (list[idx].timeLeft == 0) {
list[idx].seckillType = "1";
}
}
}
if (allChange) {
that.setData({
[param]: list,
});
}
}, 1000);
}
export const formatTimeArea = (dateStart, dateEnd, type = false) => {
dateStart = new Date(dateStart);
const year = dateStart.getFullYear();
const month = dateStart.getMonth() + 1;
const day = dateStart.getDate();
const hour = dateStart.getHours();
const minute = dateStart.getMinutes();
const second = dateStart.getSeconds();
let timeArea = [year, month, day].map(formatNumber).join("-") + " ";
switch (parseInt(hour / 6)) {
case 0:
timeArea += "00:00-06:00";
break;
case 1:
timeArea += "06:00-12:00";
break;
case 2:
timeArea += "12:00-18:00";
break;
case 3:
timeArea += "18:00-24:00";
break;
}
return timeArea;
};
export function throttleShort(fn, interval) {
var enterTime = 0;
var gapTime = interval || 200;
return function () {
var context = this;
var backTime = new Date();
if (backTime - enterTime > gapTime) {
fn.call(context, arguments);
enterTime = backTime;
}
};
}
export var accMul = function (arg1, arg2) {
if (!arg1 || !arg2) {
return 0;
}
var m = 0,
s1 = arg1.toString(),
s2 = arg2.toString();
try {
m += s1.split(".")[1].length;
} catch (e) { }
try {
m += s2.split(".")[1].length;
} catch (e) { }
let data =
(Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) /
Math.pow(10, m);
let string = data.toString();
if (string.indexOf(".") > -1) {
data = data.toFixed(2);
}
return data;
};
/**
* 身份证隐藏
* @param {*} value
* @param {*} frontLen 前面保留位数
* @param {*} endLen 后面保留位数
*/
export var hideIDCard = (value, frontLen, endLen) => {
frontLen = frontLen || 4;
endLen = endLen || 4;
if (value) {
let len = value.length - frontLen - endLen;
let xing = "";
for (let i = 0; i < len; i++) {
xing += "*";
}
return (
value.substring(0, frontLen) +
xing +
value.substring(value.length - endLen)
);
} else {
return "";
}
};
export var calculateDistance = (lat1, lon1, lat2, lon2) => {
const R = 6371; // 地球半径,单位为公里
const dLat = ((lat2 - lat1) * Math.PI) / 180;
const dLon = ((lon2 - lon1) * Math.PI) / 180;
const a =
0.5 -
Math.cos(dLat) / 2 +
(Math.cos((lat1 * Math.PI) / 180) *
Math.cos((lat2 * Math.PI) / 180) *
(1 - Math.cos(dLon))) /
2;
return R * 2 * Math.asin(Math.sqrt(a)); // 返回距离,单位为公里
};
// 格式化身份证号
export const formatCardNumber = (cardNumber) => {
if (cardNumber) {
return cardNumber.replace(/(\d{3})(\d{4})(\d{7})(\d{2})/, "$1 **** $4");
}
return "";
};
export const formatter = (type, val) => {
if (type === "year") return `${val}年`;
if (type === "month") return `${val}月`;
if (type === "day") return `${val}日`;
if (type === "hour") return `${val}时`;
if (type === "minute") return `${val}分`;
return val;
};
export const formatTimestamp = (timestamp) => {
var date = new Date(timestamp * 1000);
var year = date.getFullYear();
var month = ("0" + (date.getMonth() + 1)).slice(-2);
var day = ("0" + date.getDate()).slice(-2);
var hour = ("0" + date.getHours()).slice(-2);
var minute = ("0" + date.getMinutes()).slice(-2);
var second = ("0" + date.getSeconds()).slice(-2);
return (
year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second
);
};
/**
* 时间戳转年月日格式
* @param {number} timestamp 时间戳
* @returns {string} 格式化后的日期字符串 (YYYY-MM-DD)
*/
export const formatTimestampToDate = (timestamp, type) => {
if (!timestamp) return '';
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, "0");
const minute = String(date.getMinutes()).padStart(2, "0");
const second = String(date.getSeconds()).padStart(2, "0");
if (type === 'ymdHsm') {
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
} else {
return `${year}-${month}-${day}`
}
};
// 将字符串转换为 Base64
export const stringToBase64 = (str) => {
// 把字符串编码成 Uint8Array
const encoder = new TextEncoder();
const uint8Array = encoder.encode(str);
// 把 Uint8Array 转换为 ArrayBuffer
const arrayBuffer = uint8Array.buffer;
// 利用小程序 API 把 ArrayBuffer 转换为 Base64 字符串
return wx.arrayBufferToBase64(arrayBuffer);
};
// utils/dateRange.js
export const DateRangeUtil = {
// 格式化日期时间(精确到分钟)
formatDateTime(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hour = String(date.getHours()).padStart(2, "0");
const minute = String(date.getMinutes()).padStart(2, "0");
return `${year}-${month}-${day} ${hour}:${minute}`;
},
// 获取相对时间范围
getRelativeRange(days) {
const end = new Date();
end.setHours(23, 59, 0, 0); // 结束时间设为23:59
const start = new Date();
start.setDate(end.getDate() - days + 1);
start.setHours(0, 0, 0, 0); // 开始时间设为00:00
return {
start: this.formatDateTime(start),
end: this.formatDateTime(end),
};
},
// 昨天
yesterday() {
const end = new Date();
end.setDate(end.getDate() - 1);
end.setHours(23, 59, 0, 0); // 结束时间设为23:59
const start = new Date();
start.setDate(end.getDate());
start.setHours(0, 0, 0, 0); // 开始时间设为00:00
return {
start: this.formatDateTime(start),
end: this.formatDateTime(end),
};
},
// 近3天
last3Days() {
return this.getRelativeRange(3);
},
// 近7天
last7Days() {
return this.getRelativeRange(7);
},
// 近30天
last30Days() {
return this.getRelativeRange(30);
},
// 本周(从周一开始到周日结束)
thisWeek() {
const end = new Date();
end.setHours(23, 59, 0, 0);
const start = new Date();
start.setDate(end.getDate() - (end.getDay() || 7) + 1); // 周一是第1天
start.setHours(0, 0, 0, 0);
return {
start: this.formatDateTime(start),
end: this.formatDateTime(end),
};
},
// 本月
thisMonth() {
const end = new Date();
end.setHours(23, 59, 0, 0);
end.setMonth(end.getMonth() + 1, 0); // 本月最后一天
const start = new Date();
start.setDate(1);
start.setHours(0, 0, 0, 0);
return {
start: this.formatDateTime(start),
end: this.formatDateTime(end),
};
},
// 本季度
thisQuarter() {
const now = new Date();
const quarter = Math.floor(now.getMonth() / 3);
const start = new Date(now.getFullYear(), quarter * 3, 1, 0, 0, 0);
const end = new Date(now.getFullYear(), (quarter + 1) * 3, 0, 23, 59, 0);
return {
start: this.formatDateTime(start),
end: this.formatDateTime(end),
};
},
// 本年
thisYear() {
const year = new Date().getFullYear();
return {
start: `${year}-01-01 00:00`,
end: `${year}-12-31 23:59`,
};
},
};
/**
* 获取嵌套对象的值(支持 'address.detail' 格式路径)
* @param {Object} obj 目标对象
* @param {string} path 字段路径(如 'address.detail')
* @returns {any} 字段值
*/
function getNestedValue(obj, path) {
return path.split(".").reduce((acc, part) => {
return acc && acc.hasOwnProperty(part) ? acc[part] : null;
}, obj);
}
/**
* 通用表单校验方法
* @param {Object} formData 表单数据对象
* @param {Object} rules 校验规则配置(结构示例见下方说明)
* @returns {Object} 错误信息对象(键为字段名,值为错误提示)
*/
export function validateForm(formData, rules) {
const errors = {};
// 遍历所有校验规则
Object.keys(rules).forEach((field) => {
const fieldRules = rules[field];
let errorMsg = "";
// 支持嵌套字段(如 address.detail)
const fieldValue = getNestedValue(formData, field);
// 执行所有校验规则(遇到第一个错误即停止)
for (const rule of fieldRules) {
if (errorMsg) break; // 已有错误则跳过后续校验
// 处理必填校验
if (rule.required) {
const msg = validators.required(fieldValue, rule.message);
if (msg) {
errorMsg = msg;
continue;
}
}
if (rule.pattern) {
if (fieldValue != null && fieldValue != "") {
const isValid = new RegExp(rule.pattern.replace(/\\\\/g, "\\")).test(
fieldValue
);
if (!isValid) {
errorMsg = rule.message || "输入内容不符合要求";
}
}
}
}
errors[field] = errorMsg;
});
//遍历errors 每一项里的值为空 则去掉这个值
Object.keys(errors).forEach((key) => {
if (errors[key] === "") {
delete errors[key];
}
});
// 获取第一个错误 用于toast
const firstKey = Object.keys(errors)[0]; // 获取第一个键
const firstValue = errors[firstKey];
return {
isValid: errors && Object.keys(errors).length === 0,
errors: errors,
firstError: firstValue || "",
};
}
const validators = {
// 通用必填校验(可复用)
required: (value, message = "该字段为必填项") => {
return value ? "" : message;
},
};
/**
* 计算当前日期到指定日期的天数差
* @param {string|number|Date} targetDate - 指定日期(支持字符串、时间戳或Date对象)
* @param {Object} options - 配置项
* @param {boolean} [options.includeToday=true] - 是否包含今天(默认true)
* @param {boolean} [options.ignoreTime=true] - 是否忽略时分秒(默认true)
* @returns {number} 天数差(正值表示未来,负值表示过去)
*/
export function daysFromToday(targetDate, options = {}) {
const { includeToday = true, ignoreTime = true } = options;
// 获取当前日期
const today = new Date();
// 处理目标日期
const target =
typeof targetDate === "number"
? new Date(targetDate)
: new Date(targetDate);
// 忽略时分秒(可选)
if (ignoreTime) {
today.setHours(0, 0, 0, 0);
target.setHours(0, 0, 0, 0);
}
// 计算时间差(毫秒)
const timeDiff = target.getTime() - today.getTime();
// 转换为天数
const daysDiff = Math.floor(timeDiff / (1000 * 3600 * 24));
// 如果包含今天,需要加1天
return includeToday ? daysDiff + 1 : daysDiff;
}
export function timeDiff(date1, date2, hasNewDay=true) {
// hasNewDay是否计算今天,默认算今天
// 处理输入参数:转换为Date对象
var d1 = typeof date1 === "number" ? new Date(date1) : new Date(date1);
var d2 = typeof date2 === "number" ? new Date(date2) : new Date(date2);
// 重置时分秒为0(只比较日期部分)
d1.setHours(0, 0, 0, 0);
d2.setHours(0, 0, 0, 0);
// 如果日期部分相同,返回1天
if (d1.getTime() === d2.getTime() && hasNewDay) {
return 1;
}
// 计算时间差(毫秒)
var timeDiff = d2.getTime() - d1.getTime();
// 将时间差转换为天数(向下取整并加1)
return Math.floor(timeDiff / (1000 * 3600 * 24)) + (hasNewDay ? 1 : 0);
}
//数字范围校验 允许小数 不符合条件的不允许输入不要清空
export const validateNumberRange = (inputValue, min, max) => {
// 正则表达式:允许输入数字和小数点
const reg = /^\d*\.?\d*$/;
// 如果输入的不是数字或小数点,移除非法字符
inputValue = inputValue.replace(/[^0-9.]/g, "");
// 转换为数字进行范围校验
const numValue = parseFloat(inputValue);
// 如果转换后的值不在范围内,调整为范围内的合法值
if (!isNaN(numValue)) {
if (numValue < min) {
inputValue = min.toString();
} else if (numValue > max) {
inputValue = max.toString();
}
} else {
inputValue = ""; // 如果输入无法转换为数字,清空输入框
}
return inputValue;
};
//数字范围校验 允许输入小数点后几位 几位可配置 不符合条件不允许输入 不要清空
export const validateDecimalPlaces = (inputValue, decimalPlaces) => {
// 正则表达式:允许输入数字和小数点,限制小数点后位数
const reg = new RegExp(`^\\d*(\\.\\d{0,${decimalPlaces}})?$`);
// 如果输入的不是符合条件的格式,移除非法字符
if (!reg.test(inputValue)) {
inputValue = inputValue.replace(/[^0-9.]/g, "");
const parts = inputValue.split(".");
if (parts.length > 1) {
// 保留小数点后指定的位数
inputValue = parts[0] + "." + parts[1].slice(0, decimalPlaces);
}
}
return inputValue;
};
export const getignoreRuleTextAndRole = function (ignoreRule) {
let obj = { text: "", role: "" }
var text = "";
if (ignoreRule == 1) {
obj.dialogTitle = "不抹零";
} else if (ignoreRule == 2) {
obj.dialogTitle = "角分抹零";
obj.role = "运费:108.80元 抹零后:108元";
} else if (ignoreRule == 3) {
obj.dialogTitle = "5元以下抹零";
obj.role = "运费:108.80元 抹零后:105元,\n 运费:104.80元 抹零后:100元";
} else if (ignoreRule == 4) {
obj.dialogTitle = "10元以下抹零";
obj.role = "运费:108.80元 抹零后:100元";
}
return obj;
};
export const getSettlementTonsRuleName = (id) => {
let name = "";
if (id == 1) {
name = "按签收";
}
if (id == 2) {
name = "按最小值";
}
if (id == 3) {
name = "按发车";
}
return name
};
export const getErasureRuleName = (id) => {
let name = "";
if (id == 1) {
name = "不抹零";
}
if (id == 2) {
name = "角分抹零";
}
if (id == 3) {
name = "5元以下抹零";
}
if (id == 4) {
name = "10元以下抹零";
}
return name;
}
// 对象格式序列化
export function toFormUrlEncoded(obj) {
return Object.keys(obj)
.map((key) => encodeURIComponent(key) + "=" + encodeURIComponent(obj[key] || ''))
.join("&");
}
// 将对象中,vlaue为空值,转换为--
export const handleNullValue = (obj) => {
for (let key in obj) {
if (obj[key] === null || obj[key] === undefined || obj[key] === '') {
obj[key] = '--';
} else if (typeof obj[key] === 'object') {
handleNullValue(obj[key]);
}
}
return obj;
}
// 获取performanceId对应的文字描述
export const getPerformanceDesc = (id, channelType, channelTypeCn, isList) => {
let channelTypeStr;
if (channelType === null || channelType === undefined) {
channelTypeStr = '--';
} else if (channelType === -1) {
channelTypeStr = "不使用";
} else {
channelTypeStr = channelTypeCn ?? '-';
}
const performanceMap = {
1: isList ? '汽运入库(' + channelTypeStr + ')' : '汽运入库',
4: '直接入库',
6: '火运入库',
7: '货权转入',
8: '货权转出',
11: '火运出库',
9: '直接出库',
10: isList ? '汽运出库(' + channelTypeStr + ')' : '汽运出库'
};
return performanceMap[id] || '--';
};
export const formatTimeCommon = function (time, style) {
if (!time) {
return "--";
}
const date = new Date(time);
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
var hour = date.getHours();
var minute = date.getMinutes();
var second = date.getSeconds();
// 格式化数字为两位数
var formatNumber = function (n) {
return n < 10 ? "0" + n : n;
};
// 默认格式
if (!style) {
style = "YYYY-MM-DD HH:MM:SS";
}
// 将 style 转为大写,支持大小写混合
style = style.toUpperCase();
// 替换格式中的占位符
var formattedTime = style
.replace("YYYY", year)
.replace("MM", formatNumber(month))
.replace("DD", formatNumber(day))
.replace("HH", formatNumber(hour))
.replace("MM", formatNumber(minute)) // 注意:MM 会被替换两次
.replace("SS", formatNumber(second));
return formattedTime;
};
END...