Files
ks-app-employment-service/common/globalFunction.js
2025-09-29 11:53:10 +08:00

919 lines
27 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

import useUserStore from "../stores/useUserStore";
import {
request,
createRequest,
uploadFile
} from "../utils/request";
import streamRequest, {
chatRequest
} from "../utils/streamRequest.js";
export const CloneDeep = (props) => {
if (typeof props !== 'object' || props === null) {
return props
}
let result
if (props) {
result = []
} else {
result = {}
}
for (let key in props) {
if (props.hasOwnProperty(key)) {
result[key] = CloneDeep(props[key])
}
}
return result
}
export const msg = (title, duration = 1500, mask = false, icon = 'none', image) => {
if (Boolean(title) === false) {
return;
}
uni.showToast({
title,
duration,
mask,
icon,
image
});
}
const prePage = () => {
let pages = getCurrentPages();
let prePage = pages[pages.length - 2];
return prePage.$vm;
}
/**
* 页面跳转封装,支持 query 参数传递和返回回调
* @param {string} url - 跳转路径
* @param {object} options
* @param {boolean} options.needLogin - 是否需要登录
* @param {object} options.query - 携带参数
* @param {function} options.onBack - 页面返回时的回调(目标页调用 uni.navigateBack 时传递数据)
*/
export const navTo = function(url, {
needLogin = false,
query = {},
onBack = null
} = {}) {
const userStore = useUserStore();
if (needLogin && !userStore.hasLogin) {
uni.navigateTo({
url: '/pages/login/login'
});
return;
}
const queryStr = Object.entries(query)
.map(([key, val]) => `${key}=${encodeURIComponent(val)}`)
.join('&');
const finalUrl = queryStr ? `${url}?${queryStr}` : url;
if (onBack) {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
currentPage.__onBackCallback__ = onBack;
}
uni.navigateTo({
url: finalUrl
});
};
export const navBack = function({
delta = 1,
data = null,
fallbackUrl = '/pages/index/index'
} = {}) {
const pages = getCurrentPages();
if (pages.length > 1) {
const prevPage = pages[pages.length - 1 - delta];
// 如果上一页存在回调函数,调用
if (data && prevPage?.__onBackCallback__) {
prevPage.__onBackCallback__(data);
}
uni.navigateBack({
delta
});
} else {
// 没有可返回的页面,直接跳转 fallback 页面
uni.reLaunch({
url: fallbackUrl
});
}
};
// // 默认返回上一页
// navBack();
// // 返回上两层
// navBack(2);
// // 没有历史页面时跳转首页
// navBack(1, '/pages/home/home');
function getdeviceInfo() {
const globalData = {
statusBarHeight: 0, // 状态导航栏高度
topHeight: 0, // 距离顶部高度
navHeight: 0, // 总体高度
windowHeight: 0, // 可使用窗口高度
tabBarHight: 0, //底部导航栏高度
};
let systemInfo = uni.getSystemInfoSync()
globalData.windowHeight = systemInfo.screenHeight
// 底部导航栏
globalData.tabBarHight = systemInfo.screenHeight - systemInfo.safeArea.bottom
// 状态栏高度
globalData.statusBarHeight = systemInfo.statusBarHeight
// #ifdef MP-MP-WEIXIN
let menuButtonInfo = uni.getMenuButtonBoundingClientRect()
// 胶囊距离顶部高度
globalData.topHeight = menuButtonInfo.top
// 胶囊高度
globalData.navHeight = menuButtonInfo.height
// #endif
return {
...globalData
}
}
function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time))
}
const cloneDeep = (obj) => {
// 1.1 判断是否是对象
const isObject = (obj) => (typeof obj === 'object' || typeof obj === 'function') && obj !== 'null'
if (!isObject(obj)) {
throw new Error('参数不是对象')
}
// 1.3 如果参数为数组,则复制数组各元素,否则复制对象属性
const newObject = Array.isArray(obj) ? [...obj] : {
...obj
}
// 1.4 迭代
Object.keys(newObject).forEach((key) => {
// 1.5 判断如果遍历到的属性值为对象则继续递归cloneDeep
if (isObject(newObject[key])) {
newObject[key] = cloneDeep(newObject[key])
}
})
return newObject
}
const CopyText = (text) => {
let input = document.createElement('textarea');
input.value = text;
document.body.appendChild(input);
input.select();
let flag = document.execCommand('copy')
if (flag) {
message.success('成功复制到剪贴板')
} else {
message.success('复制失败')
}
document.body.removeChild(input)
}
// 柯里化 降低使用范围,提高适用性
function Exp(regExp) {
return (str) => {
return regExp.test(str)
}
}
const checkingPhoneRegExp = Exp(/^1[3-9]{1}\d{9}/)
// 手机号校验 checkingPhoneRegExp(phone)
const checkingEmailRegExp = Exp(/^[a-z0-9_\.-]+@[a-z0-9_\.-]+[a-z0-9]{2,6}$/i)
// 邮箱校验 checkingEmailRegExp(email)
function throttle(fn, delay = 300) {
let valid = true
let savedArgs = null // 参数存储器
let savedContext = null // 上下文存储器
return function(...args) {
// 保存当前参数和上下文
savedArgs = args
savedContext = this
if (!valid) return false
valid = false
setTimeout(() => {
fn.apply(savedContext, savedArgs)
valid = true
savedArgs = null // 清空存储
savedContext = null
}, delay)
}
}
function debounce(fun, delay) {
return function(args) {
let that = this
let _args = args
clearTimeout(fun.id)
fun.id = setTimeout(function() {
fun.call(that, _args)
}, delay)
}
}
function toRad(degree) {
return degree * Math.PI / 180;
}
function haversine(lat1, lon1, lat2, lon2) {
const R = 6371; // 地球半径,单位为公里
const a1 = toRad(lat1);
const a2 = toRad(lat2);
const b1 = toRad(lat2 - lat1);
const b2 = toRad(lon2 - lon1);
const a = Math.sin(b1 / 2) * Math.sin(b1 / 2) +
Math.cos(a1) * Math.cos(a2) * Math.sin(b2 / 2) * Math.sin(b2 / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c; // 计算得到的距离,单位为公里
return distance;
}
export function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {
const R = 6371; // 地球平均半径,单位为公里
const dLat = deg2rad(lat2 - lat1);
const dLon = deg2rad(lon2 - lon1);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const d = R * c;
return {
km: d,
m: d * 1000
};
}
// 将角度转换为弧度
function deg2rad(deg) {
return deg * (Math.PI / 180);
}
function vacanciesTo(vacancies) {
if (vacancies >= 0) {
return vacancies + "人"
} else {
return '不限人数'
}
}
function salaryGlobal(type = 'min') {
const salay = [2, 5, 10, 15, 20, 25, 30, 50, 80];
const salaymax = [2, 5, 10, 15, 20, 25, 30, 50, 80, 100];
const salarys = salay.map((item, index) => ({
label: item + 'k',
value: item * 1000,
children: CloneDeep(salaymax).splice(index).map((vItem) => ({
label: vItem + 'k',
value: vItem * 1000,
}))
}))
return salarys
}
class CustomSystem {
constructor() {
const systemInfo = uni.getSystemInfoSync();
this.systemInfo = systemInfo
}
}
const customSystem = new CustomSystem()
function setCheckedNodes(nodes, ids) {
const isClear = ids.length === 0;
nodes.forEach((firstLayer) => {
// 每次处理都先重置
firstLayer.checkednumber = 0;
const traverse = (node) => {
if (isClear) {
node.checked = false;
} else {
node.checked = ids.includes(node.id);
}
if (node !== firstLayer && node.checked) {
firstLayer.checkednumber++;
}
if (node.children && node.children.length) {
node.children.forEach(child => traverse(child));
}
};
traverse(firstLayer);
});
return nodes;
}
const formatTotal = (total) => {
if (total < 10) return total.toString(); // 直接返回小于 10 的数
const magnitude = Math.pow(10, Math.floor(Math.log10(total))); // 计算数量级
const roundedTotal = Math.floor(total / magnitude) * magnitude; // 去掉零头
return `${roundedTotal}+`;
};
export function formatDate(isoString) {
const date = new Date(isoString);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,需要 +1
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}`;
}
export function insertSortData(data, attribute = 'createTime') {
const sortedData = data.sort((a, b) => new Date(b[attribute]) - new Date(a[attribute])); // 按时间降序排序
const result = [];
let lastDate = '';
let lastTitle = ''
const now = new Date();
const todayStr = now.toISOString().split('T')[0]; // 获取今天的日期字符串
const yesterday = new Date(now.setDate(now.getDate() - 1)).toISOString().split('T')[0]; // 获取昨天的日期字符串
const twoDaysAgo = new Date(now.setDate(now.getDate() - 1)).toISOString().split('T')[0]; // 获取前天的日期字符串
sortedData.forEach(item => {
const itemAttribute = item[attribute].replace('T', ' ')
const itemDate = itemAttribute.split(' ')[0]; // 提取日期部分
let title = itemDate;
if (itemDate === todayStr) {
title = '今天';
} else if (itemDate === yesterday) {
title = '昨天';
} else if (itemDate === twoDaysAgo) {
title = '前天';
}
if (lastDate !== itemDate) {
result.push({
title,
isTitle: true
});
lastDate = itemDate;
lastTitle = title;
}
result.push({
...item,
isTitle: false
});
});
return [result, lastTitle];
}
function getWeeksOfMonth(year, month) {
const firstDay = new Date(year, month - 1, 1); // 当月第一天
const lastDay = new Date(year, month, 0); // 当月最后一天
const weeks = [];
let week = [];
for (let d = new Date(firstDay); d <= lastDay; d.setDate(d.getDate() + 1)) {
// 补充第一周的上个月日期
if (week.length === 0 && d.getDay() !== 1) {
let prevMonday = new Date(d);
prevMonday.setDate(d.getDate() - (d.getDay() === 0 ? 6 : d.getDay() - 1));
while (prevMonday < d) {
week.push({
year: prevMonday.getFullYear(),
month: prevMonday.getMonth() + 1,
day: prevMonday.getDate(),
fullDate: getLocalYYYYMMDD(prevMonday), // 修正
weekday: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][prevMonday.getDay()],
isCurrent: false // 上个月日期
});
prevMonday.setDate(prevMonday.getDate() + 1);
}
}
// 添加当前月份的日期
week.push({
year: d.getFullYear(),
month: d.getMonth() + 1,
day: d.getDate(),
fullDate: getLocalYYYYMMDD(d), // 修正
weekday: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][d.getDay()],
isCurrent: true // 当前月的日期
});
// 如果到了月末但当前周未满7天需要补足到周日
if (d.getTime() === lastDay.getTime() && week.length < 7) {
let nextDay = new Date(d);
nextDay.setDate(d.getDate() + 1);
while (week.length < 7) {
week.push({
year: nextDay.getFullYear(),
month: nextDay.getMonth() + 1,
day: nextDay.getDate(),
fullDate: getLocalYYYYMMDD(nextDay), // 修正
weekday: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][nextDay.getDay()],
isCurrent: false // 下个月日期
});
nextDay.setDate(nextDay.getDate() + 1);
}
}
// 如果本周满了7天或者到了月末
if (week.length === 7 || d.getTime() === lastDay.getTime()) {
weeks.push([...week]); // 存入当前周
week = []; // 清空,准备下一周
}
}
return weeks;
}
// 新增工具函数:将日期格式化为本地 YYYY-MM-DD 字符串
function getLocalYYYYMMDD(date) {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
}
function isFutureDate(dateStr) {
const inputDate = new Date(dateStr);
const today = new Date();
// 只比较年月日,不考虑具体时间
today.setHours(0, 0, 0, 0);
inputDate.setHours(0, 0, 0, 0);
return inputDate > today;
}
function parseQueryParams(url = window.location.href) {
const queryString = url.split('?')[1]?.split('#')[0];
const params = {};
if (!queryString) return params;
queryString.split('&').forEach(param => {
const [key, value] = param.split('=');
if (key) {
params[decodeURIComponent(key)] = decodeURIComponent(value || '');
}
});
return params;
}
function formatFileSize(bytes) {
if (bytes < 1024) return bytes + ' B'
else if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB'
else if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(2) + ' MB'
else return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB'
}
function sendingMiniProgramMessage(data = {
text: 'hello'
}, action = 'defalut') {
jWeixin.miniProgram.postMessage({
data,
action
});
}
function copyText(text) {
uni.setClipboardData({
data: text,
showToast: false,
success(res) {
msg('复制成功')
},
});
}
function appendScriptTagElement(src) {
if (!src) return null;
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.onload = () => {
resolve()
};
script.onerror = () => {
reject()
};
document.body.appendChild(script);
})
}
function isInWechatMiniProgramWebview() {
const ua = navigator.userAgent.toLowerCase()
return ua.includes('miniprogram') || window.__wxjs_environment === 'miniprogram'
}
function isEmptyObject(obj) {
return obj && typeof obj === 'object' && !Array.isArray(obj) && Object.keys(obj).length === 0;
}
/**
* 身份证号码校验工具
* 支持15位和18位身份证号码校验
* 提供详细的校验结果和错误信息
*/
export const IdCardValidator = {
// 每位加权因子
powers: ['7', '9', '10', '5', '8', '4', '2', '1', '6', '3', '7', '9', '10', '5', '8', '4', '2'],
// 第18位校检码
parityBit: ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'],
// 省市地区码映射
provinceAndCitys: {
11: '北京',
12: '天津',
13: '河北',
14: '山西',
15: '内蒙古',
21: '辽宁',
22: '吉林',
23: '黑龙江',
31: '上海',
32: '江苏',
33: '浙江',
34: '安徽',
35: '福建',
36: '江西',
37: '山东',
41: '河南',
42: '湖北',
43: '湖南',
44: '广东',
45: '广西',
46: '海南',
50: '重庆',
51: '四川',
52: '贵州',
53: '云南',
54: '西藏',
61: '陕西',
62: '甘肃',
63: '青海',
64: '宁夏',
65: '新疆',
71: '台湾',
81: '香港',
82: '澳门',
91: '国外'
},
/**
* 验证身份证号码
* @param {string} idCardNo - 身份证号码
* @returns {Object} 校验结果 { valid: boolean, message: string, info: Object }
*/
validate(idCardNo) {
// 检查是否为空
if (this._isEmpty(idCardNo)) {
return this._createResult(false, '身份证号码不能为空');
}
// 去除空格
idCardNo = idCardNo.trim();
// 检查长度支持15位和18位
if (idCardNo.length === 15) {
return this._validate15IdCardNo(idCardNo);
} else if (idCardNo.length === 18) {
return this._validate18IdCardNo(idCardNo);
} else {
return this._createResult(false, '身份证号码长度必须是15位或18位');
}
},
/**
* 验证18位身份证号码
* @param {string} idCardNo - 18位身份证号码
* @returns {Object} 校验结果
*/
_validate18IdCardNo(idCardNo) {
// 18位身份证号码的基本格式校验
if (!/^[1-9]\d{5}[1-9]\d{3}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}(\d|x|X)$/.test(
idCardNo)) {
return this._createResult(false, '身份证号码格式不正确');
}
// 校验地址码
const addressCode = idCardNo.substring(0, 6);
const addressResult = this._checkAddressCode(addressCode);
if (!addressResult.valid) {
return addressResult;
}
// 校验日期码
const birDayCode = idCardNo.substring(6, 14);
const birthResult = this._checkBirthDayCode(birDayCode);
if (!birthResult.valid) {
return birthResult;
}
// 验证校检码
if (!this._checkParityBit(idCardNo)) {
return this._createResult(false, '身份证号码校验码错误');
}
// 提取身份证信息
const info = this._extractInfo(idCardNo, 18);
return this._createResult(true, '身份证号码校验通过', info);
},
/**
* 验证15位身份证号码
* @param {string} idCardNo - 15位身份证号码
* @returns {Object} 校验结果
*/
_validate15IdCardNo(idCardNo) {
// 15位身份证号码的基本格式校验
if (!/^[1-9]\d{5}\d{2}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}$/.test(idCardNo)) {
return this._createResult(false, '身份证号码格式不正确');
}
// 校验地址码
const addressCode = idCardNo.substring(0, 6);
const addressResult = this._checkAddressCode(addressCode);
if (!addressResult.valid) {
return addressResult;
}
// 校验日期码15位身份证年份是两位这里转换为四位
const year = `19${idCardNo.substring(6, 8)}`; // 15位身份证一般是1900年以后出生
const month = idCardNo.substring(8, 10);
const day = idCardNo.substring(10, 12);
const birDayCode = `${year}${month}${day}`;
const birthResult = this._checkBirthDayCode(birDayCode);
if (!birthResult.valid) {
return birthResult;
}
// 提取身份证信息
const info = this._extractInfo(idCardNo, 15);
return this._createResult(true, '身份证号码校验通过', info);
},
/**
* 校验地址码
* @param {string} addressCode - 地址码
* @returns {Object} 校验结果
*/
_checkAddressCode(addressCode) {
if (!/^[1-9]\d{5}$/.test(addressCode)) {
return this._createResult(false, '地址码格式不正确');
}
const provinceCode = parseInt(addressCode.substring(0, 2));
if (!this.provinceAndCitys[provinceCode]) {
return this._createResult(false, '地址码对应的地区不存在');
}
return this._createResult(true);
},
/**
* 校验日期码
* @param {string} birDayCode - 日期码 (格式YYYYMMDD)
* @returns {Object} 校验结果
*/
_checkBirthDayCode(birDayCode) {
if (!/^[1-9]\d{3}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))$/.test(birDayCode)) {
return this._createResult(false, '出生日期格式不正确');
}
const year = parseInt(birDayCode.substring(0, 4), 10);
const month = parseInt(birDayCode.substring(4, 6), 10);
const day = parseInt(birDayCode.substring(6), 10);
// 检查年份范围(合理的出生年份范围)
const currentYear = new Date().getFullYear();
if (year < 1900 || year > currentYear) {
return this._createResult(false, '出生年份超出合理范围');
}
// 检查日期有效性
const date = new Date(year, month - 1, day);
if (
date.getFullYear() !== year ||
date.getMonth() !== month - 1 ||
date.getDate() !== day
) {
return this._createResult(false, '出生日期无效');
}
// 检查是否为未来日期
if (date > new Date()) {
return this._createResult(false, '出生日期不能是未来日期');
}
return this._createResult(true);
},
/**
* 验证校检码
* @param {string} idCardNo - 18位身份证号码
* @returns {boolean} 校验结果
*/
_checkParityBit(idCardNo) {
const parityBit = idCardNo.charAt(17).toUpperCase();
return this._getParityBit(idCardNo) === parityBit;
},
/**
* 计算校检码
* @param {string} idCardNo - 18位身份证号码
* @returns {string} 校检码
*/
_getParityBit(idCardNo) {
const id17 = idCardNo.substring(0, 17);
let power = 0;
for (let i = 0; i < 17; i++) {
power += parseInt(id17.charAt(i), 10) * parseInt(this.powers[i], 10);
}
const mod = power % 11;
return this.parityBit[mod];
},
/**
* 提取身份证信息
* @param {string} idCardNo - 身份证号码
* @param {number} type - 类型 15或18
* @returns {Object} 身份证信息
*/
_extractInfo(idCardNo, type) {
let addressCode, birthYear, birthMonth, birthDay, genderCode, gender;
// 地址码
addressCode = idCardNo.substring(0, 6);
const provinceCode = parseInt(addressCode.substring(0, 2));
const province = this.provinceAndCitys[provinceCode] || '';
// 出生日期
if (type === 18) {
birthYear = idCardNo.substring(6, 10);
birthMonth = idCardNo.substring(10, 12);
birthDay = idCardNo.substring(12, 14);
genderCode = idCardNo.substring(14, 17);
} else {
birthYear = `19${idCardNo.substring(6, 8)}`;
birthMonth = idCardNo.substring(8, 10);
birthDay = idCardNo.substring(10, 12);
genderCode = idCardNo.substring(12, 15);
}
// 性别
gender = parseInt(genderCode, 10) % 2 === 1 ? '男' : '女';
return {
addressCode,
province,
birthday: `${birthYear}-${birthMonth}-${birthDay}`,
gender,
length: type
};
},
/**
* 检查是否为空
* @param {*} value - 要检查的值
* @returns {boolean} 是否为空
*/
_isEmpty(value) {
return value === null || value === undefined || value === '';
},
/**
* 创建校验结果对象
* @param {boolean} valid - 是否有效
* @param {string} message - 消息
* @param {Object} info - 附加信息
* @returns {Object} 校验结果
*/
_createResult(valid, message = '', info = {}) {
return {
valid,
message,
info
};
},
/**
* 将15位身份证升级为18位
* @param {string} idCardNo - 15位身份证号码
* @returns {string|boolean} 18位身份证号码或false(无效的15位身份证)
*/
upgrade15To18(idCardNo) {
if (idCardNo.length !== 15) {
return false;
}
// 先验证15位身份证是否有效
const validateResult = this._validate15IdCardNo(idCardNo);
if (!validateResult.valid) {
return false;
}
// 转换出生年份为4位
const year = `19${idCardNo.substring(6, 8)}`;
const rest = idCardNo.substring(8);
// 拼接17位
const id17 = `${idCardNo.substring(0, 6)}${year}${rest}`;
// 计算校验码
let power = 0;
for (let i = 0; i < 17; i++) {
power += parseInt(id17.charAt(i), 10) * parseInt(this.powers[i], 10);
}
const mod = power % 11;
const parityBit = this.parityBit[mod];
// 拼接18位身份证
return `${id17}${parityBit}`;
}
};
export const $api = {
msg,
prePage,
sleep,
request,
createRequest,
streamRequest,
chatRequest,
insertSortData,
uploadFile,
formatFileSize,
sendingMiniProgramMessage,
copyText
}
export default {
$api,
navTo,
navBack,
cloneDeep,
formatDate,
IdCardValidator,
getdeviceInfo,
checkingPhoneRegExp,
checkingEmailRegExp,
throttle,
debounce,
haversine,
getDistanceFromLatLonInKm,
vacanciesTo,
salaryGlobal,
customSystem,
setCheckedNodes,
formatTotal,
getWeeksOfMonth,
isFutureDate,
parseQueryParams,
appendScriptTagElement,
insertSortData,
isInWechatMiniProgramWebview,
isEmptyObject,
}