634 lines
14 KiB
JavaScript
634 lines
14 KiB
JavaScript
import dayjs from 'dayjs';
|
|
var api = require('../config/api.js');
|
|
|
|
const formatTime = (date, template) => dayjs(date).format(template);
|
|
|
|
const formatNumber = n => {
|
|
n = n.toString()
|
|
return n[1] ? n : '0' + n
|
|
}
|
|
|
|
function formatTimeNum(number, format) {
|
|
|
|
var formateArr = ['Y', 'M', 'D', 'h', 'm', 's'];
|
|
var returnArr = [];
|
|
|
|
var date = new Date(number * 1000);
|
|
returnArr.push(date.getFullYear());
|
|
returnArr.push(formatNumber(date.getMonth() + 1));
|
|
returnArr.push(formatNumber(date.getDate()));
|
|
|
|
returnArr.push(formatNumber(date.getHours()));
|
|
returnArr.push(formatNumber(date.getMinutes()));
|
|
returnArr.push(formatNumber(date.getSeconds()));
|
|
|
|
for (var i in returnArr) {
|
|
format = format.replace(formateArr[i], returnArr[i]);
|
|
}
|
|
return format;
|
|
}
|
|
|
|
function testMobile(num) {
|
|
console.log
|
|
var myreg = /^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1})|(17[0-9]{1})|(16[0-9]{1})|(19[0-9]{1}))+\d{8})$/;
|
|
if (num.length == 0) {
|
|
wx.showToast({
|
|
title: '手机号为空',
|
|
image: '/images/icon/icon_error.png',
|
|
})
|
|
return false;
|
|
} else if (num.length < 11) {
|
|
wx.showToast({
|
|
title: '手机号长度有误!',
|
|
image: '/images/icon/icon_error.png',
|
|
})
|
|
return false;
|
|
} else if (!myreg.test(num)) {
|
|
wx.showToast({
|
|
title: '手机号有误!',
|
|
image: '/images/icon/icon_error.png',
|
|
})
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
const REQUEST_TIMEOUT = 15000;
|
|
let tokenCache = '';
|
|
let tokenLoaded = false;
|
|
let reloginTask = null;
|
|
|
|
function getAppSafe() {
|
|
try {
|
|
return getApp();
|
|
} catch (err) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** 内存缓存 token,避免每次请求都读 Storage */
|
|
function getToken() {
|
|
if (!tokenLoaded) {
|
|
tokenCache = wx.getStorageSync('token') || '';
|
|
tokenLoaded = true;
|
|
}
|
|
return tokenCache;
|
|
}
|
|
|
|
function setToken(token) {
|
|
tokenCache = token || '';
|
|
tokenLoaded = true;
|
|
if (tokenCache) {
|
|
wx.setStorageSync('token', tokenCache);
|
|
} else {
|
|
wx.removeStorageSync('token');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 本地还没有 token 时,等冷启动登录结束再发需要鉴权的请求
|
|
* 已有 token 则直接发,过期由 401 重登处理
|
|
*/
|
|
function ensureLogin() {
|
|
if (getToken()) {
|
|
return Promise.resolve();
|
|
}
|
|
const app = getAppSafe();
|
|
if (app && app.loginReady) {
|
|
return app.loginReady;
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
function isUnauthorized(statusCode, body) {
|
|
return statusCode === 401 || !!(body && body.code === 401);
|
|
}
|
|
|
|
/** 登录失效后重新换 token,进行中的重登合并成一次 */
|
|
function relogin() {
|
|
const app = getAppSafe();
|
|
if (app && app.loggingIn && app.loginReady) {
|
|
return app.loginReady.then(() => !!getToken());
|
|
}
|
|
if (reloginTask) {
|
|
return reloginTask;
|
|
}
|
|
reloginTask = login()
|
|
.then((wxRes) => request(api.AuthLoginByWeixin, { code: wxRes.code }, 'POST', { auth: false }))
|
|
.then((res) => {
|
|
if (res && res.code === 1 && res.data && res.data.token) {
|
|
setToken(res.data.token);
|
|
if (res.data.userinfo) {
|
|
wx.setStorageSync('userInfo', res.data.userinfo);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
})
|
|
.catch((err) => {
|
|
console.error('relogin failed:', err);
|
|
return false;
|
|
})
|
|
.then((ok) => {
|
|
reloginTask = null;
|
|
return ok;
|
|
});
|
|
return reloginTask;
|
|
}
|
|
|
|
/**
|
|
* 统一请求
|
|
* @param {string} url
|
|
* @param {object} data
|
|
* @param {string} method
|
|
* @param {{auth?: boolean, timeout?: number, retried?: boolean}} options
|
|
* auth 默认 true:没有 token 时等待登录,401 时重登并重试一次
|
|
* auth 为 false:免登录接口(分类、首页配置等)立即发送,不等待登录
|
|
*/
|
|
function request(url, data = {}, method = 'GET', options = {}) {
|
|
const auth = options.auth !== false;
|
|
const timeout = options.timeout || REQUEST_TIMEOUT;
|
|
const retried = !!options.retried;
|
|
|
|
const send = () => new Promise((resolve, reject) => {
|
|
const header = { 'Content-Type': 'application/json' };
|
|
const token = getToken();
|
|
if (token) {
|
|
header.token = token;
|
|
}
|
|
wx.request({
|
|
url,
|
|
data,
|
|
method,
|
|
timeout,
|
|
header,
|
|
success(res) {
|
|
const body = res.data && typeof res.data === 'object'
|
|
? res.data
|
|
: { code: 0, msg: '网络异常', data: null };
|
|
if (res.statusCode === 200 || isUnauthorized(res.statusCode, body)) {
|
|
resolve({ unauthorized: isUnauthorized(res.statusCode, body), body });
|
|
return;
|
|
}
|
|
reject((body && body.msg) || res.errMsg || '网络异常');
|
|
},
|
|
fail(err) {
|
|
reject(err);
|
|
},
|
|
});
|
|
});
|
|
|
|
const dispatch = () => send().then((result) => {
|
|
if (result.unauthorized && auth && !retried) {
|
|
return relogin().then((ok) => {
|
|
if (!ok) {
|
|
return result.body;
|
|
}
|
|
return request(url, data, method, { auth: true, timeout, retried: true });
|
|
});
|
|
}
|
|
return result.body;
|
|
});
|
|
|
|
if (!auth) {
|
|
return dispatch();
|
|
}
|
|
return ensureLogin().then(dispatch);
|
|
}
|
|
|
|
/**
|
|
* 检查微信会话是否过期
|
|
*/
|
|
function checkSession() {
|
|
return new Promise(function (resolve, reject) {
|
|
wx.checkSession({
|
|
success: function () {
|
|
resolve(true);
|
|
},
|
|
fail: function () {
|
|
reject(false);
|
|
}
|
|
})
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 调用微信登录
|
|
*/
|
|
function login() {
|
|
return new Promise(function (resolve, reject) {
|
|
wx.login({
|
|
success: function (res) {
|
|
if (res.code) {
|
|
//登录远程服务器
|
|
resolve(res);
|
|
} else {
|
|
reject(res);
|
|
}
|
|
},
|
|
fail: function (err) {
|
|
reject(err);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function getUserInfo() {
|
|
return new Promise(function (resolve, reject) {
|
|
wx.getUserInfo({
|
|
withCredentials: true,
|
|
success: function (res) {
|
|
resolve(res);
|
|
},
|
|
fail: function (err) {
|
|
reject(err);
|
|
}
|
|
})
|
|
});
|
|
}
|
|
|
|
function redirect(url) {
|
|
|
|
//判断页面是否需要登录
|
|
if (false) {
|
|
wx.redirectTo({
|
|
url: '/pages/auth/login/login'
|
|
});
|
|
return false;
|
|
} else {
|
|
wx.redirectTo({
|
|
url: url
|
|
});
|
|
}
|
|
}
|
|
|
|
function showErrorToast(msg) {
|
|
wx.showToast({
|
|
title: msg,
|
|
icon: 'none',
|
|
})
|
|
}
|
|
|
|
function showSuccessToast(msg) {
|
|
wx.showToast({
|
|
title: msg,
|
|
icon: 'success',
|
|
})
|
|
}
|
|
|
|
function loginNow() {
|
|
let userInfo = wx.getStorageSync('userInfo');
|
|
if (userInfo == '') {
|
|
wx.login({
|
|
success: (res) => {
|
|
request(api.AuthLoginByWeixin, {
|
|
code: res.code
|
|
}, 'POST', { auth: false }).then(function (res) {
|
|
if (res.code === 1) {
|
|
let userInfo = res.data.userInfo;
|
|
setToken(res.data.token);
|
|
wx.setStorageSync('userInfo', userInfo);
|
|
}
|
|
});
|
|
},
|
|
});
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function getTextLength(str, full) {
|
|
let len = 0;
|
|
for (let i = 0; i < str.length; i++) {
|
|
let c = str.charCodeAt(i);
|
|
//单字节加1
|
|
if ((c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f)) {
|
|
len++;
|
|
} else {
|
|
len += (full ? 2 : 1);
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* rgba(255, 255, 255, 1) => #ffffff
|
|
* @param {String} color
|
|
*/
|
|
function transferColor(color = '') {
|
|
let res = '#';
|
|
color = color.replace(/^rgba?\(/, '').replace(/\)$/, '');
|
|
color = color.split(', ');
|
|
|
|
color.length > 3 ? color.length = 3 : '';
|
|
for (let item of color) {
|
|
item = parseInt(item || 0);
|
|
if (item < 10) {
|
|
res += ('0' + item)
|
|
} else {
|
|
res += (item.toString(16))
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
function transferBorder(border = '') {
|
|
let res = border.match(/(\w+)px\s(\w+)\s(.*)/);
|
|
let obj = {};
|
|
|
|
if (res) {
|
|
obj = {
|
|
width: +res[1],
|
|
style: res[2],
|
|
color: res[3]
|
|
}
|
|
}
|
|
|
|
return res ? obj : null;
|
|
}
|
|
|
|
|
|
/**
|
|
* 内边距,依次为上右下左
|
|
* @param {*} padding
|
|
*/
|
|
function transferPadding(padding = '0 0 0 0') {
|
|
padding = padding.split(' ');
|
|
for (let i = 0, len = padding.length; i < len; i++) {
|
|
padding[i] = +padding[i].replace('px', '');
|
|
}
|
|
|
|
return padding;
|
|
}
|
|
/**
|
|
* type1: 0, 25, 17, rgba(0, 0, 0, 0.3)
|
|
* type2: rgba(0, 0, 0, 0.3) 0px 25px 17px 0px => (0, 25, 17, rgba(0, 0, 0, 0.3))
|
|
* @param {*} shadow
|
|
*/
|
|
function transferBoxShadow(shadow = '', type) {
|
|
if (!shadow || shadow === 'none') return;
|
|
let color;
|
|
let split;
|
|
|
|
split = shadow.match(/(\w+)\s(\w+)\s(\w+)\s(rgb.*)/);
|
|
|
|
if (split) {
|
|
split.shift();
|
|
shadow = split;
|
|
color = split[3] || '#ffffff';
|
|
} else {
|
|
split = shadow.split(') ');
|
|
color = split[0] + ')'
|
|
shadow = split[1].split('px ');
|
|
}
|
|
|
|
return {
|
|
offsetX: +shadow[0] || 0,
|
|
offsetY: +shadow[1] || 0,
|
|
blur: +shadow[2] || 0,
|
|
color
|
|
}
|
|
}
|
|
|
|
function getUid(prefix) {
|
|
prefix = prefix || '';
|
|
|
|
return (
|
|
prefix +
|
|
'xxyxxyxx'.replace(/[xy]/g, c => {
|
|
let r = (Math.random() * 16) | 0;
|
|
let v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
return v.toString(16);
|
|
})
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 格式化金额为字符串,单位为元
|
|
* @param price 金额,单位元
|
|
* @param fill 小数位数 0-不填充 1-一位 2-两位
|
|
*/
|
|
function priceFormat(price, fill = 0) {
|
|
if (isNaN(price) || price === null || price === Infinity) {
|
|
return price;
|
|
}
|
|
|
|
const rounded = Math.round(parseFloat(`${price}`) * 100) / 100;
|
|
let priceFormatValue = `${rounded}`;
|
|
if (fill > 0) {
|
|
if (priceFormatValue.indexOf('.') === -1) {
|
|
priceFormatValue = `${priceFormatValue}.`;
|
|
}
|
|
const decimal = priceFormatValue.split('.')[1] || '';
|
|
const n = fill - decimal.length;
|
|
for (let i = 0; i < n; i++) {
|
|
priceFormatValue = `${priceFormatValue}0`;
|
|
}
|
|
}
|
|
return priceFormatValue;
|
|
}
|
|
|
|
/**
|
|
* 获取cdn裁剪后链接
|
|
*
|
|
* @param {string} url 基础链接
|
|
* @param {number} width 宽度,单位px
|
|
* @param {number} [height] 可选,高度,不填时与width同值
|
|
*/
|
|
const cosThumb = (url, width, height = width) => {
|
|
if (url.indexOf('?') > -1) {
|
|
return url;
|
|
}
|
|
|
|
if (url.indexOf('http://') === 0) {
|
|
url = url.replace('http://', 'https://');
|
|
}
|
|
|
|
return `${url}?imageMogr2/thumbnail/${~~width}x${~~height}`;
|
|
};
|
|
|
|
const get = (source, paths, defaultValue) => {
|
|
if (typeof paths === 'string') {
|
|
paths = paths
|
|
.replace(/\[/g, '.')
|
|
.replace(/\]/g, '')
|
|
.split('.')
|
|
.filter(Boolean);
|
|
}
|
|
const {
|
|
length
|
|
} = paths;
|
|
let index = 0;
|
|
while (source != null && index < length) {
|
|
source = source[paths[index++]];
|
|
}
|
|
return source === undefined || index === 0 ? defaultValue : source;
|
|
};
|
|
let systemWidth = 0;
|
|
/** 获取系统宽度,为了减少启动消耗所以在函数里边做初始化 */
|
|
export const loadSystemWidth = () => {
|
|
if (systemWidth) {
|
|
return systemWidth;
|
|
}
|
|
|
|
try {
|
|
({
|
|
screenWidth: systemWidth,
|
|
pixelRatio
|
|
} = wx.getSystemInfoSync());
|
|
} catch (e) {
|
|
systemWidth = 0;
|
|
}
|
|
return systemWidth;
|
|
};
|
|
|
|
/**
|
|
* 转换rpx为px
|
|
*
|
|
* @description
|
|
* 什么时候用?
|
|
* - 布局(width: 172rpx)已经写好, 某些组件只接受px作为style或者prop指定
|
|
*
|
|
*/
|
|
const rpx2px = (rpx, round = false) => {
|
|
loadSystemWidth();
|
|
|
|
// px / systemWidth = rpx / 750
|
|
const result = (rpx * systemWidth) / 750;
|
|
|
|
if (round) {
|
|
return Math.floor(result);
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* 手机号码*加密函数
|
|
* @param {string} phone 电话号
|
|
* @returns
|
|
*/
|
|
const phoneEncryption = (phone) => {
|
|
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
|
};
|
|
|
|
// 内置手机号正则字符串
|
|
const innerPhoneReg =
|
|
'^1(?:3\\d|4[4-9]|5[0-35-9]|6[67]|7[0-8]|8\\d|9\\d)\\d{8}$';
|
|
|
|
/**
|
|
* 手机号正则校验
|
|
* @param phone 手机号
|
|
* @param phoneReg 正则字符串
|
|
* @returns true - 校验通过 false - 校验失败
|
|
*/
|
|
const phoneRegCheck = (phone) => {
|
|
const phoneRegExp = new RegExp(innerPhoneReg);
|
|
return phoneRegExp.test(phone);
|
|
};
|
|
|
|
const obj2Params = (obj = {}, encode = false) => {
|
|
const result = [];
|
|
Object.keys(obj).forEach((key) =>
|
|
result.push(`${key}=${encode ? encodeURIComponent(obj[key]) : obj[key]}`),
|
|
);
|
|
|
|
return result.join('&');
|
|
};
|
|
|
|
/**
|
|
* 标记某条需求需要在列表页刷新状态
|
|
* 用 globalData 传递,不依赖页面栈深度(backAndRefresh 的 delta 在多层跳转后容易算错上一页)
|
|
* @param {number|string} demandId 需求ID
|
|
*/
|
|
function markDemandRefresh(demandId) {
|
|
const app = getApp();
|
|
if (app && app.globalData) {
|
|
app.globalData.refreshDemandId = Number(demandId) || 0;
|
|
}
|
|
}
|
|
|
|
function backAndRefresh(delta = 1, freshField = 'freshPage') {
|
|
const pages = getCurrentPages(); // 获取页面栈
|
|
const prevPage = pages[pages.length - (delta + 1)]; // 上一页实例
|
|
if (prevPage) {
|
|
prevPage.setData({ [freshField]: true });
|
|
}
|
|
wx.navigateBack({ delta }); // 返回上一页
|
|
}
|
|
|
|
function checkSubscriptionsSetting() {
|
|
wx.getSetting({
|
|
withSubscriptions: true,
|
|
success(res) {
|
|
console.log('getSetting:', res);
|
|
const itemSettings = res.subscriptionsSetting.itemSettings || {};
|
|
let hasReject = false;
|
|
if (res.subscriptionsSetting.mainSwitch === false) {
|
|
hasReject = true;
|
|
}
|
|
for (const id in itemSettings) {
|
|
if (itemSettings[id] === 'reject') {
|
|
hasReject = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hasReject) {
|
|
wx.showToast({
|
|
title: '请授权订阅消息,用于接收重要消息',
|
|
icon: 'none',
|
|
duration: 3000
|
|
});
|
|
wx.openSetting({
|
|
withSubscriptions: true,
|
|
success(openRes) {
|
|
console.log('openSetting:', openRes);
|
|
},
|
|
fail(err) {
|
|
console.error('打开设置页失败:', err);
|
|
}
|
|
});
|
|
} else {
|
|
console.log('所有消息均未被拒绝');
|
|
}
|
|
},
|
|
fail(err) {
|
|
console.error('获取设置失败:', err);
|
|
}
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
formatTime,
|
|
priceFormat,
|
|
cosThumb,
|
|
get,
|
|
rpx2px,
|
|
phoneEncryption,
|
|
phoneRegCheck,
|
|
formatTimeNum: formatTimeNum,
|
|
request,
|
|
getToken,
|
|
setToken,
|
|
redirect,
|
|
showErrorToast,
|
|
showSuccessToast,
|
|
checkSession,
|
|
login,
|
|
getUserInfo,
|
|
testMobile,
|
|
loginNow,
|
|
getTextLength,
|
|
transferBorder,
|
|
transferColor,
|
|
transferPadding,
|
|
transferBoxShadow,
|
|
obj2Params,
|
|
backAndRefresh,
|
|
markDemandRefresh,
|
|
checkSubscriptionsSetting,
|
|
getUid
|
|
}; |