# tms-core

# index

tms-core入口文件

点击查看源码
 /**
 * @module: index
 * @desc: tms-core入口文件
 */
import runtime from './runtime/index';
import Request from './request';
import AutoReport from './report/proxy/index';
import Reporter from './report/index';
import { getConfig, getApolloConfig } from './config';
import syncApi from './syncfnmanager';
import nav from './navigator';
import { getLogManager, getRealtimeLogManager } from './log';
import { setEnvInfo, getEnvInfo, setAuthInfo, setAppPagePaths, isAppPageExist, getHomePage } from './env';
import md5 from './md5';
import { callCloudFunc } from './cloudService';
import { getEventDispatcher } from './eventDispatcher';
import { serialize } from './objUtils';
import { calCoordinateDistance, formatDistance } from './distanceUtils';
import { rpxToPx, pxToRpx } from './rpx';
import { getNavBarConfigData } from './navbarUtils';
import { compareVersion } from './compareVersion';
import { formatPlate, subStr, hidePhoneCenter, isValidPhone, isValidPlate, isValidAuthCode, roundStr, } from './stringUtils';
import { round, } from './numUtils';
import { groupTimeDuration, formatTimeDuration, formatTime, formatTimeStr, formatTimeWithDetails, formatDateTime, dateToString, parseDateTime, } from './timeUtils';
import { ipxInit, isIPX, getIpxClass, getIpxConfig, } from './ipxHelper';
import getLocInstance from './location/index';
import LocationBase from './location/base';
import { getMpOpenId, getOuterOpenId, getEncryptUserInfo } from './mpInfo';
import * as storage from './storage';
import * as uiUtil from './tmsuiUtils';
import { throttle, debounce } from './funcUtils';
/**
 * @public
 * @description 创建网络请求对象,用于向腾讯出行服务平台后台发送网络请求
 * @param {Object} [config] 参数配置
 * @param {Boolean} [config.withAuth=true] 是否填充登录态参数
 * @param {String} [config.host] 自定义的host域名
 * @param {Object} [config.baseParam] 默认携带的参数
 * @returns {Object} [Request实例](#class-request)
 * @example
 * const $ = getApp().tms.createRequest();
 * $.get(apiPath)
 *   .then((resp) => {
 *     // ...
 *   })
 *   .catch((err) => {
 *     // ...
 *   });
 */
const createRequest = (config = {}) => new Request(config);
/**
 * @description 埋点上报
 * @returns {Object} 包含[report方法](#report)的对象
 * @example
 * getReporter().report2('当前页面|组件的唯一标志', {});
 */
const getReporter = () => Reporter;
/**
 * @description 获取地理位置方法的集合
 * @returns {Class} [Location类](#class-location)
 */
const getLocationManager = () => getLocInstance();
/**
 * @description 获取位置信息base类(业务无关)
 * @returns {Class} [Location类](#class-location)
 */
const getLocationBaseClass = () => LocationBase;
/**
 * @description init core包初始化, 小程序已在app.js中对齐初始化
 * @param {Object} options 初始化
 * @param {String} options.appVersion 小程序本次发版的版本号
 * @param {String} options.wxAppId appId
 * @param {String} options.secretKey 密钥
 * @param {String} options.client 小程序名
 * @param {String} options.appEnv 环境名称 production、development、test、predist
 * @param {Array} options.appPagePaths  小程序包含的所有页面路径
 * @param {String} options.homePage 首页的相关配置 { path: "/page/**", isTab: false }
 * @param {String} options.cloudEnvId 云函数的环境id
 * @param {String} options.defaultHost 小程序请求调用接口的域名,所有请求都会走腾讯出行网关服务,腾讯出行网关在转发到相关服务
 * @returns {void} 无返回值.
 */
const init = (options = {}) => {
    const { appVersion, wxAppId, secretKey = '', client, defaultHost, cloudEnvId, appEnv, appPagePaths, homePage, } = options;
    const envInfo = {
        wxAppId,
        appVersion,
        appEnv,
        client,
        cloudEnvId,
    };
    setEnvInfo(envInfo);
    setAppPagePaths(appPagePaths, homePage);
    Reporter.init(envInfo);
    AutoReport.init();
    Request.defaultHost = defaultHost;
    Request.defaultSecretKey = secretKey;
    // 初始化云环境
    wx.cloud.init({ env: cloudEnvId });
};
/**
 * @description 获取用户位置信息 -- 兼容版 现有业务中有的使用promise, 有的没有, 做一下兼容
 * @returns { Promise } 位置信息
 */
const getUserLocation = () => {
    const userLocation = getLocInstance().getUserLocation();
    if (userLocation) {
        return Promise.resolve(userLocation);
    }
    return getLocInstance().getLocationDetail(false);
};
/**
 * @namespace api
 * @description 对外暴露的api
 * @param {Function} init 初始化方法
 * @param {Function} createRequest 创建Request的实例的方法
 * @param {Function} getReporter 获取上报管理器
 * @param {Function} getLocationManager 获取地理位置管理器
 * @param {Function} setUserLocation 设置用户位置信息
 * @param {Function} getUserLocation 获取用户位置信息
 * @param {Function} cloudService... 云函数相关处理查看 [cloudService](#clouddservice)
 * @param {Function} compareVersion... 版本比较查看 [compareVersion](#compareversion)
 * @param {Function} config... 配置相关函数查看 [config](#config)
 * @param {Function} env... 环境相关处理查看 [env](#env)
 * @param {Function} eventdispatcher... 事件管理相关处理 [eventdispatcher](#eventdispatcher)
 * @param {Function} ipxHelper... iphonex相关方法查看 [ipxHelper](#ipxhelper)
 * @param {Function} log... 上报相关方法查看 [log](#log)
 * @param {Function} md5... md5加密查看 [md5](#md5)
 * @param {Function} mpInfo... 服务接入相关接口查看 [mpInfo](#mpinfo)
 * @param {Function} navbarUtils... 获取导航高度的相关函数查看 [navbarUtils](#navbarutils)
 * @param {Function} navigator... 小程序跳转相关方法 [navigator](#navigator)
 * @param {Function} numUtils... 数字相关处理查看 [numUtils](#numutils)
 * @param {Function} objUtils... 对象相关处理查看[objUtils](#objutils)
 * @param {Function} request... 请求相关处理查看 [request](#request)
 * @param {Function} rpx... px与rpx的相互转化查看 [rpx](#rpx)
 * @param {Function} runtime...  集成业务通用接口查看 [runtime-index](#runtime-index)
 * @param {Function} storage... 本地缓存查看 [storage](#storage)
 * @param {Function} stringUtils... 字符串相关处理查看 [stringUtils](#stringutils)
 * @param {Function} syncfnmanager... 小程序调用wx同步方法的管理查看 [syncfnmanager](#syncfnmanager)
 * @param {Function} timeUtils... 数字相关处理查看 [timeUtils](#timeutils)
 */
const api = {
    init,
    createRequest,
    setAuthInfo,
    getLogManager,
    getRealtimeLogManager,
    md5,
    getReporter,
    getLocationManager,
    getLocationBaseClass,
    getEventDispatcher,
    getEnvInfo,
    getConfig,
    getApolloConfig,
    navigateToWebview: nav.navigateToWebview,
    isAppPageExist,
    getHomePage,
    callCloudFunc,
    setUserLocation: (loc) => {
        getLocInstance().setUserLocation(loc);
    },
    getUserLocation,
    /* 字符串方法 */
    formatPlate,
    subStr,
    hidePhoneCenter,
    isValidPhone,
    isValidPlate,
    isValidAuthCode,
    roundStr,
    /* 数字方法 */
    round,
    /* 时间方法 */
    groupTimeDuration,
    formatTimeDuration,
    formatTime,
    formatTimeStr,
    formatTimeWithDetails,
    formatDateTime,
    dateToString,
    parseDateTime,
    /* IPX方法 */
    ipxInit,
    isIPX,
    getIpxClass,
    getIpxConfig,
    /* 处理对象方法 */
    serialize,
    /* 距离方法 */
    calCoordinateDistance,
    formatDistance,
    /* 获取外部合作商openid */
    getMpOpenId,
    getOuterOpenId,
    /* 获取加密的用户信息 */
    getEncryptUserInfo,
    rpxToPx,
    pxToRpx,
    compareVersion,
    getNavBarConfigData,
    storage,
    ...syncApi,
    ...uiUtil,
    throttle,
    debounce,
    ...runtime,
};
export default api;
 

# namespace api

对外暴露的api

参数

字段名 类型 说明 默认值 是否可选
init function 初始化方法 ---
createRequest function 创建Request的实例的方法 ---
getReporter function 获取上报管理器 ---
getLocationManager function 获取地理位置管理器 ---
setUserLocation function 设置用户位置信息 ---
getUserLocation function 获取用户位置信息 ---
cloudService... function 云函数相关处理查看 cloudService ---
compareVersion... function 版本比较查看 compareVersion ---
config... function 配置相关函数查看 config ---
env... function 环境相关处理查看 env ---
eventdispatcher... function 事件管理相关处理 eventdispatcher ---
ipxHelper... function iphonex相关方法查看 ipxHelper ---
log... function 上报相关方法查看 log ---
md5... function md5加密查看 md5 ---
mpInfo... function 服务接入相关接口查看 mpInfo ---
navbarUtils... function 获取导航高度的相关函数查看 navbarUtils ---
navigator... function 小程序跳转相关方法 navigator ---
numUtils... function 数字相关处理查看 numUtils ---
objUtils... function 对象相关处理查看objUtils ---
request... function 请求相关处理查看 request ---
rpx... function px与rpx的相互转化查看 rpx ---
runtime... function 集成业务通用接口查看 runtime-index ---
storage... function 本地缓存查看 storage ---
stringUtils... function 字符串相关处理查看 stringUtils ---
syncfnmanager... function 小程序调用wx同步方法的管理查看 syncfnmanager ---
timeUtils... function 数字相关处理查看 timeUtils ---

# Object createRequest

创建网络请求对象,用于向腾讯出行服务平台后台发送网络请求

参数

字段名 类型 说明 默认值 是否可选
config Object 参数配置 ---
config.withAuth Boolean 是否填充登录态参数 ---
config.host String 自定义的host域名 ---
config.baseParam Object 默认携带的参数 ---

返回值

类型 说明
Object Request实例

示例代码

Example

const $ = getApp().tms.createRequest();
$.get(apiPath)
  .then((resp) => {
    // ...
  })
  .catch((err) => {
    // ...
  });

# Object getReporter

埋点上报

返回值

类型 说明
Object 包含report方法的对象

示例代码

Example

getReporter().report2('当前页面|组件的唯一标志', {});

# Class getLocationManager

获取地理位置方法的集合

返回值

类型 说明
Class Location类

# Class getLocationBaseClass

获取位置信息base类(业务无关)

返回值

类型 说明
Class Location类

# void init

init core包初始化, 小程序已在app.js中对齐初始化

参数

字段名 类型 说明 默认值 是否可选
options Object 初始化 ---
options.appVersion String 小程序本次发版的版本号 ---
options.wxAppId String appId ---
options.secretKey String 密钥 ---
options.client String 小程序名 ---
options.appEnv String 环境名称 production、development、test、predist ---
options.appPagePaths Array 小程序包含的所有页面路径 ---
options.homePage String 首页的相关配置 { path: "/page/**", isTab: false } ---
options.cloudEnvId String 云函数的环境id ---
options.defaultHost String 小程序请求调用接口的域名,所有请求都会走腾讯出行网关服务,腾讯出行网关在转发到相关服务 ---

返回值

类型 说明
void 无返回值.

# Promise getUserLocation

获取用户位置信息 -- 兼容版 现有业务中有的使用promise, 有的没有, 做一下兼容

返回值

类型 说明
Promise 位置信息

# cloudService

小程序调用云函数相关方法

点击查看源码
 /**
 * @desc: 小程序调用云函数相关方法
 */
import { getEnvInfo, getAuthInfo } from './env';
const logger = wx.getLogManager({});
/**
 * callCloudFunc 方法 调用云函数
 * @param {String} name 要调用的云函数
 * @param {Object} data 需要传给云函数的参数
 * @param {Boolean} withAuth 是否需要补充userId,token参数,当云函数需要调用SinanServer时,需要这些参数来完成鉴权
 * @returns {viod} 云函数
 * @example
 * const { tms } = getApp({ allowDefault: true });
 * tms.callCloudFunc('user', { $url: 'user/getState', ...param }, true).then(({ result }) => result)
 */
export const callCloudFunc = async (name = '', data = {}, withAuth = true) => {
    const { cloudEnvId, appVersion } = getEnvInfo();
    const timestamp = Date.now();
    const random = Math.random()
        .toString()
        .slice(2, 7);
    const sourceId = 7; // 6 未知 7 云函数 8 出行 9 我的车
    const mergedData = {
        ...data,
        timestamp,
        seqId: `${timestamp}${sourceId}${random}`,
        appVersion, // 补充appVersion参数
    };
    if (withAuth) { // 补充userId, token参数
        const { userId, token } = await getAuthInfo();
        Object.assign(mergedData, { userId, token });
    }
    const res = await new Promise((resolve, reject) => {
        wx.cloud.callFunction({
            name,
            data: mergedData,
            config: { env: cloudEnvId },
            success: (res) => {
                let result = JSON.stringify(res);
                if (result.length > 500) {
                    result = `${result.substring(0, 500)} 内容太长被截断`;
                }
                const obj = {
                    name,
                    env: cloudEnvId,
                    params: JSON.stringify(mergedData),
                    res: result,
                };
                const str = JSON.stringify(obj, null, '  ').replace(/\\"/ig, '\'');
                logger.log(`云函数请求成功:\n${str}`);
                resolve(res);
            },
            fail: (err) => {
                const obj = {
                    name,
                    env: cloudEnvId,
                    params: JSON.stringify(mergedData),
                    err: JSON.stringify(err),
                };
                const str = JSON.stringify(obj, null, '  ').replace(/\\"/ig, '\'');
                logger.warn(`云函数请求失败:\n${str}`);
                reject({ err, name, mergedData });
            },
        });
    });
    return res;
};
 

# viod callCloudFunc

callCloudFunc 方法 调用云函数

参数

字段名 类型 说明 默认值 是否可选
name String 要调用的云函数 ---
data Object 需要传给云函数的参数 ---
withAuth Boolean 是否需要补充userId,token参数,当云函数需要调用SinanServer时,需要这些参数来完成鉴权 ---

返回值

类型 说明
viod 云函数

示例代码

Example

const { tms } = getApp({ allowDefault: true });
tms.callCloudFunc('user', { $url: 'user/getState', ...param }, true).then(({ result }) => result)

# compareVersion

点击查看源码
 /**
 * 版本比较函数
 * @param {String} sourceVersion 作为基准版本号
 * @param {String} targetVersion 目标比较版本号
 * @returns {Number} 比较结果
 * 返回值说明:
 * 1 : 大于基准版本号
 * 0 : 等于基准版本号
 * -1: 小于基准版本号
 */
export const compareVersion = (sourceVersion, targetVersion) => {
    if (typeof sourceVersion !== 'string' || typeof targetVersion !== 'string') {
        throw new Error('版本比较参数类型有误');
    }
    const toInt = n => parseInt(n, 10); // eslint-disable-line require-jsdoc
    const sourceArray = sourceVersion.split('.').map(toInt);
    const targetArray = targetVersion.split('.').map(toInt);
    for (let i = 0; i < sourceArray.length; i += 1) {
        if (sourceArray[i] > targetArray[i]) {
            return 1;
        }
        if (sourceArray[i] < targetArray[i]) {
            return -1;
        }
    }
    return 0;
};
 

# Number compareVersion

版本比较函数

参数

字段名 类型 说明 默认值 是否可选
sourceVersion String 作为基准版本号 ---
targetVersion String 目标比较版本号 ---

返回值

类型 说明
Number 比较结果返回值说明:1 : 大于基准版本号0 : 等于基准版本号-1: 小于基准版本号

# config

小程获取配置相关接口

点击查看源码
 /**
 * @desc: 小程获取配置相关接口
 */
import Request from './request';
import { getEnvInfo } from './env';
const parseAllCfgs = (configPaths, resData, defaultCfgs) => configPaths.map((path, index) => {
    const found = resData.find(cfg => cfg.configPath === path);
    if (found) {
        return JSON.parse(found.configValue);
    }
    if (defaultCfgs?.[index]) {
        return defaultCfgs[index];
    }
    return {}; // 没找到配置,返回一个空对象
});
const formatConfigPaths = (configPath) => {
    const configPaths = Array.isArray(configPath) ? configPath : [configPath];
    const { client } = getEnvInfo();
    configPaths.forEach((path, index) => {
        configPaths[index] = path.replace(/\$\{client\}/, client);
    });
    return configPaths;
};
/**
 * getConfig 批量拉取配置
 * @description 拉取运营平台上的配置内容。关于运营平台的具体用法,参见{@link https://iwiki.woa.com/pages/viewpage.action?pageId=527948584}
 * @example <caption>拉取单个配置</caption>
 * const { tms } = getApp({ allowDefault: true });
 * const cfg = await tms.getConfig('/${client}/violation/subscribe', {}, { title: '当前城市不支持订阅'})
 * console.log(cfg); // 成功则返回服务端存储的配置,失败返回默认值
 * @example <caption>批量拉取配置</caption>
const { tms } = getApp({ allowDefault: true });
const cfgs = await tms.getConfig([
  '/${client}/home/service',
  '/${client}/home/navbar',
], {}, [
  [
    { caption: '违章代缴', icon: 'violation.png' }
  ],
  { title: '晚上好,欢迎~' }
]);
console.log(cfgs); // 成功则返回服务端存储的配置,失败返回默认值
 * @param {String|Array} configPath 配置路径,单个路径或配置路径数组,支持替换${client}为当前小程序。例如在出行小程序,${client}会变替换为sinan
 * @param {Object} extendAttr 扩展属性,传给配置服务用于检索哪个版本的配置适用于当前用户。
 * @param {Object} [defaultCfg] 默认配置,请求失败返回默认值。
 * @returns {Promise<Object|Array<Object>>}
 * 有默认值时,请求失败返回默认值。无默认值时,请求失败返回Promise.reject
 */
function getConfig(configPath, extendAttr = {}, defaultCfg) {
    if (Array.isArray(configPath)) { // 复数形式
        if (defaultCfg) { // 有默认值
            if (!Array.isArray(defaultCfg) || configPath.length !== defaultCfg.length) {
                throw new Error('配置路径和默认值的数组长度不一致');
            }
        }
    }
    const configPaths = formatConfigPaths(configPath);
    const defaultCfgs = (defaultCfg && (Array.isArray(defaultCfg) ? defaultCfg : [defaultCfg])) || null;
    const extendAttrs = typeof extendAttr === 'string' ? extendAttr : JSON.stringify(extendAttr);
    return Request.getInstance().post('marketing/config', {
        extendAttrs,
        configPaths,
    })
        .then((res = {}) => {
        const { errCode, resData } = res || {};
        if (resData && resData.length > 0) {
            const parsed = parseAllCfgs(configPaths, resData, defaultCfgs);
            if (Array.isArray(configPath)) {
                return parsed;
            }
            return parsed[0];
        }
        if (defaultCfgs) {
            return Array.isArray(configPath) ? defaultCfgs : defaultCfgs[0];
        }
        return Promise.reject({ errCode, resData, msg: `获取${configPaths.join(',')}配置失败,接口调用出错` });
    })
        .catch((e) => {
        if (defaultCfgs) {
            return Promise.resolve(Array.isArray(configPath) ? defaultCfgs : defaultCfgs[0]);
        }
        return Promise.reject({ err: e, msg: `获取${configPaths.join(',')}配置失败,接口调用出错` });
    });
}
/**
* getApolloConfig 获取阿波罗平台配置
* @description 拉取阿波罗的配置内容。参见{@link http://mapollo.woa.com/#/26fdbf3}
* @example <caption>拉取单个配置</caption>
* const { tms } = getApp({ allowDefault: true });
* const cfg = await tms.getApolloConfig('sinan_me_faq', {}, { name: '默认配置'})
* console.log(cfg); // 成功则返回服务端存储的配置,失败返回默认值
* @param {String|Array} configPath 配置路径,单个路径或配置路径数组,支持替换${client}为当前小程序。例如在出行小程序,${client}会变替换为sinan
* @param {Object} extendAttr 扩展属性,用于自定义的过滤条件,保留字段 businessKey、moduleKey。
* @param {Object} [defaultCfg] 默认配置,请求失败返回默认值。
* @returns {Promise<Object|Array<Object>>}
* 有默认值时,请求失败返回默认值。无默认值时,请求失败返回Promise.reject
*/
function getApolloConfig(configPath, extendAttr = {}, defaultCfg) {
    const configPaths = formatConfigPaths(configPath);
    const defaultCfgs = (defaultCfg && (Array.isArray(defaultCfg) ? defaultCfg : [defaultCfg])) || null;
    const { businessKey, moduleKey, ...extendAttrs } = extendAttr;
    return Request.getInstance().post('marketing/apollo/config', {
        businessKey,
        moduleKey,
        configPaths,
        extendAttrs: JSON.stringify(extendAttrs),
    })
        .then((res) => {
        const { errCode, resData = {} } = res || {};
        const { status, msg, data } = resData;
        if (errCode === 0 && status === 0) {
            return data
                .sort((a, b) => 
            // eslint-disable-next-line implicit-arrow-linebreak
            configPaths.findIndex(configPath => configPath === a.configKey)
                - configPaths.findIndex(configPath => configPath === b.configKey))
                .map((item) => {
                const newItem = {
                    ...item.content,
                };
                Object.keys(item).forEach((key) => {
                    if (key !== 'content') {
                        newItem[`_${key}`] = item[key];
                    }
                });
                return newItem;
            });
        }
        ;
        throw new Error(msg);
    })
        .catch((e) => {
        if (defaultCfgs)
            return Promise.resolve(defaultCfgs);
        return Promise.reject({ err: e, msg: `获取${configPaths.join(',')}配置失败` });
    });
}
export { getConfig, getApolloConfig, };
 

# Promise.<(Object|Array.<Object>)> getConfig

拉取运营平台上的配置内容。关于运营平台的具体用法,参见link (opens new window)

参数

字段名 类型 说明 默认值 是否可选
configPath String|Array 配置路径,单个路径或配置路径数组,支持替换${client}为当前小程序。例如在出行小程序,${client}会变替换为sinan ---
extendAttr Object 扩展属性,传给配置服务用于检索哪个版本的配置适用于当前用户。 ---
defaultCfg Object 默认配置,请求失败返回默认值。 ---

返回值

类型 说明
Promise.<(Object|Array.<Object>)> 有默认值时,请求失败返回默认值。无默认值时,请求失败返回Promise.reject

示例代码

Example (拉取单个配置)

const { tms } = getApp({ allowDefault: true });
const cfg = await tms.getConfig('/${client}/violation/subscribe', {}, { title: '当前城市不支持订阅'})
console.log(cfg); // 成功则返回服务端存储的配置,失败返回默认值

Example (批量拉取配置)

const { tms } = getApp({ allowDefault: true });
const cfgs = await tms.getConfig([
  '/${client}/home/service',
  '/${client}/home/navbar',
], {}, [
  [
    { caption: '违章代缴', icon: 'violation.png' }
  ],
  { title: '晚上好,欢迎~' }
]);
console.log(cfgs); // 成功则返回服务端存储的配置,失败返回默认值

# Promise.<(Object|Array.<Object>)> getApolloConfig

拉取阿波罗的配置内容。参见link (opens new window)

参数

字段名 类型 说明 默认值 是否可选
configPath String|Array 配置路径,单个路径或配置路径数组,支持替换${client}为当前小程序。例如在出行小程序,${client}会变替换为sinan ---
extendAttr Object 扩展属性,用于自定义的过滤条件,保留字段 businessKey、moduleKey。 ---
defaultCfg Object 默认配置,请求失败返回默认值。 ---

返回值

类型 说明
Promise.<(Object|Array.<Object>)> 有默认值时,请求失败返回默认值。无默认值时,请求失败返回Promise.reject

示例代码

Example (拉取单个配置)

const { tms } = getApp({ allowDefault: true });
const cfg = await tms.getApolloConfig('sinan_me_faq', {}, { name: '默认配置'})
console.log(cfg); // 成功则返回服务端存储的配置,失败返回默认值

# distanceUtils

点击查看源码
 /**
 * 经纬度距离计算
 */
import { roundStr } from './stringUtils';
// 将角度换算为弧度
function convertDegreesToRadians(degrees) {
    return (degrees * Math.PI) / 180;
}
function haverSin(theta) {
    const val = Math.sin(theta / 2);
    return val * val;
}
// 计算两点间距离,单位 m
function calCoordinateDistance(a, b) {
    // 地球半径 平均值,千米
    const earthRadius = 6371;
    const lat1 = convertDegreesToRadians(a.latitude);
    const lon1 = convertDegreesToRadians(a.longitude);
    const lat2 = convertDegreesToRadians(b.latitude);
    const lon2 = convertDegreesToRadians(b.longitude);
    // 差值
    const vLon = Math.abs(lon1 - lon2);
    const vLat = Math.abs(lat1 - lat2);
    const h = haverSin(vLat) + (Math.cos(lat1) * Math.cos(lat2) * haverSin(vLon));
    const distance = 2 * earthRadius * Math.asin(Math.sqrt(h));
    return distance * 1000;
}
/**
 * 对距离进行格式化
 * @param {Number} distance 距离,单位米
 * @param {Number} keep 保留几位小数
 * @param {Boolean} removeTrailingZero 是否移除字符串末尾的无效数字0
 * @param {String} unit 单位风格类型:en-英文-m/km,EN-英文大写-M/KM,zh-中文-米/千米,ZH-中文-米/公里
 * @returns 格式化后的字符串;distance非法时返回空字符串
 */
function formatDistance(distance, keep = 2, removeTrailingZero = false, unit = 'en') {
    const distanceNum = parseFloat(String(distance));
    if (isNaN(distanceNum))
        return '';
    // 数字部分格式化
    const num = distanceNum >= 1000 ? distanceNum / 1000 : distanceNum;
    const numStr = roundStr(num, keep, removeTrailingZero);
    // 单位部分格式化
    let units;
    switch (unit) {
        case 'zh':
            units = ['米', '千米'];
            break;
        case 'ZH':
            units = ['米', '公里'];
            break;
        case 'EN':
            units = ['M', 'KM'];
            break;
        default:
            units = ['m', 'km'];
            break;
    }
    const unitStr = units[distanceNum >= 1000 ? 1 : 0];
    return `${numStr}${unitStr}`;
}
export { calCoordinateDistance, formatDistance, };
 

# formatDistance

对距离进行格式化

参数

字段名 类型 说明 默认值 是否可选
distance Number 距离,单位米 ---
keep Number 保留几位小数 ---
removeTrailingZero Boolean 是否移除字符串末尾的无效数字0 ---
unit String 单位风格类型:en-英文-m/km,EN-英文大写-M/KM,zh-中文-米/千米,ZH-中文-米/公里 ---

返回值

类型 说明
格式化后的字符串;distance非法时返回空字符串

# env

小程序运行环境相关接口

点击查看源码
 /**
 * @desc: 小程序运行环境相关接口
 */
/**
 * @description 公共对象-环境变量env
 * @namespace Env
 * @param {String} wxAppId 当前运行时小程序的appId
 * @param {String} appVersion 当前运行时所在项目的版本号
 * @param {String} appEnv 运行环境 test - 测试、production - 正式
 * @param {String} client 运行时项目名,sinan - 腾讯出行服务;
 * @param {String} cloudEnvId 运行云函数的环境id
 */
const env = {
    wxAppId: '',
    appVersion: '',
    appEnv: '',
    client: '',
    cloudEnvId: '',
};
let baseAuthInfo = undefined;
const getAuthInfoQueue = [];
/**
 * @description 设置用户信息
 * @param {Object} authInfo 用户登录状态信息
 * @param {Object} err 当获取用户信息失败时,设置错误信息
 * @returns {Void} 无返回值
 * @example
 * setAuthInfo({
    firstLogin: 0
    openId: "ou8xs5Uo4MHCETZ_P7IUwOAxvr-M"
    token: "d8ffecd9317376bd1de3b49e1fa0e3bb"
    uid: "407210826"
    unionId: "oFghpwpqGK18ixIlNGy34Y28gCb0"
 * })
 */
export const setAuthInfo = (authInfo, err) => {
    baseAuthInfo = authInfo;
    while (getAuthInfoQueue.length) {
        const pro = getAuthInfoQueue.shift();
        if (err) {
            pro.reject(err);
        }
        else {
            pro.resolve(baseAuthInfo);
        }
    }
};
/**
 * @description 获取登录状态信息
 * @returns {Object} 获取当前用户登录状态参数(见上)
 * @example
 * const data = await getAuthInfo()
  {
    firstLogin: 0 // 是否第一次登录
    openId: "ou8xs5Uo4MHCETZ_P7IUwOAxvr-M"
    token: "d8ffecd9317376bd1de3b49e1fa0e3bb"
    uid: "407210826"
    unionId: "oFghpwpqGK18ixIlNGy34Y28gCb0"
  }
 */
export const getAuthInfo = async () => {
    if (baseAuthInfo !== undefined) {
        return baseAuthInfo;
    }
    return new Promise((resolve, reject) => getAuthInfoQueue.push({ resolve, reject }));
};
/**
 * @description 用于获取运行时所在的环境信息
 * @returns {Object} 运行时环境信息 (返回值见上Env:namepace)
 */
export const getEnvInfo = () => env;
/**
 * 设置环境变量
 * @param {Object} 运行时环境信息 (入参见上Env:namepace)
 * @returns {Void} 无.
 */
export const setEnvInfo = (envInfo) => {
    const { wxAppId, appVersion, appEnv, client, cloudEnvId } = envInfo;
    env.wxAppId = wxAppId;
    env.appVersion = appVersion;
    env.appEnv = appEnv;
    env.client = client;
    env.cloudEnvId = cloudEnvId;
};
let appPagePaths = []; // app页面路径集合
let homePageInfo = null; // 首页信息 { isTab, path, tabs }
/**
 * 获取app内所有页面的路径
 * @returns {Array<String>} 页面路径集合
 */
export const getAppPagePaths = () => appPagePaths;
/**
 * 设置app内所有页面的路径
 * @param {Array<String>} paths    页面路径集合
 * @param {Object|null}   homePage 首页信息
 * @param {Boolean}       homePath.isTab 首页是否tab页
 * @param {String}        homePath.path  首页页面路径(isTab=false时使用此字段)
 * @param {Array<String>} homePath.tabs  tab页面路径列表(isTab=true时使用自此段)
 * @returns {void}
 */
export const setAppPagePaths = (paths, homePage) => {
    appPagePaths = paths;
    const { isTab = false, path = '', tabs = [] } = homePage || {};
    homePageInfo = { isTab, path, tabs };
};
/**
 * 判断页面是否存在于当前app中
 * @param {String} page 页面路径
 * @returns {Boolean} 页面是否存在app中
 */
export const isAppPageExist = (page) => {
    const route = !page ? '' : String(page).split('?')[0];
    if (!route || !Array.isArray(appPagePaths))
        return false;
    const routeWithoutPrefixSlash = route[0] === '/' ? route.substring(1) : route;
    return appPagePaths.some(path => path === routeWithoutPrefixSlash || path === `/${routeWithoutPrefixSlash}`);
};
// 以下变量标识各小程序appId
const MOBILITY_APPID = 'wx65cc950f42e8fff1'; // 出行服务小程序AppId
const MOBILITY_DEMO_APPID = 'wxa7ce727b525f80b0'; // 出行服务demo小程序AppId
const SINAN_HOME = '/modules/home/pages/index/index'; // 出行首页地址
const MYCAR_HOME = '/modules/car/index/index'; // 我的车首页地址
/**
 * 获取首页信息
 * @returns {Object|null}   homePage 首页信息
 * @returns {Boolean}       homePath.isTab 首页是否tab页
 * @returns {String}        homePath.path  首页页面路径(isTab=false时使用此字段)
 * @returns {Array<String>} homePath.tabs  tab页面路径列表(isTab=true时使用自此段)
 */
export const getHomePage = () => {
    if (homePageInfo)
        return homePageInfo;
    // 返回默认信息
    const mpAppId = wx.getAccountInfoSync()?.miniProgram?.appId;
    const homePagePath = [MOBILITY_APPID, MOBILITY_DEMO_APPID].indexOf(mpAppId) > -1 ? SINAN_HOME : MYCAR_HOME;
    homePageInfo = { isTab: false, path: homePagePath, tabs: [] };
    return homePageInfo;
};
 

# namespace Env

公共对象-环境变量env

参数

字段名 类型 说明 默认值 是否可选
wxAppId String 当前运行时小程序的appId ---
appVersion String 当前运行时所在项目的版本号 ---
appEnv String 运行环境 test - 测试、production - 正式 ---
client String 运行时项目名,sinan - 腾讯出行服务; ---
cloudEnvId String 运行云函数的环境id ---

# Void setAuthInfo

设置用户信息

参数

字段名 类型 说明 默认值 是否可选
authInfo Object 用户登录状态信息 ---
err Object 当获取用户信息失败时,设置错误信息 ---

返回值

类型 说明
Void 无返回值

示例代码

Example

setAuthInfo({
    firstLogin: 0
    openId: "ou8xs5Uo4MHCETZ_P7IUwOAxvr-M"
    token: "d8ffecd9317376bd1de3b49e1fa0e3bb"
    uid: "407210826"
    unionId: "oFghpwpqGK18ixIlNGy34Y28gCb0"
})

# Object getAuthInfo

获取登录状态信息

返回值

类型 说明
Object 获取当前用户登录状态参数(见上)

示例代码

Example

const data = await getAuthInfo()
  {
    firstLogin: 0 // 是否第一次登录
    openId: "ou8xs5Uo4MHCETZ_P7IUwOAxvr-M"
    token: "d8ffecd9317376bd1de3b49e1fa0e3bb"
    uid: "407210826"
    unionId: "oFghpwpqGK18ixIlNGy34Y28gCb0"
  }

# Object getEnvInfo

用于获取运行时所在的环境信息

返回值

类型 说明
Object 运行时环境信息 (返回值见上Env:namepace)

# Void setEnvInfo

设置环境变量

参数

字段名 类型 说明 默认值 是否可选
运行时环境信息 Object (入参见上Env:namepace) ---

返回值

类型 说明
Void 无.

# Array.<String> getAppPagePaths

获取app内所有页面的路径

返回值

类型 说明
Array.<String> 页面路径集合

# void setAppPagePaths

设置app内所有页面的路径

参数

字段名 类型 说明 默认值 是否可选
paths Array.<String> 页面路径集合 ---
homePage Object|null 首页信息 ---
homePath.isTab Boolean 首页是否tab页 ---
homePath.path String 首页页面路径(isTab=false时使用此字段) ---
homePath.tabs Array.<String> tab页面路径列表(isTab=true时使用自此段) ---

返回值

类型 说明
void

# Boolean isAppPageExist

判断页面是否存在于当前app中

参数

字段名 类型 说明 默认值 是否可选
page String 页面路径 ---

返回值

类型 说明
Boolean 页面是否存在app中

# Objectnull getHomePage

获取首页信息

返回值

类型 说明
Object|null homePage 首页信息
Boolean homePath.isTab 首页是否tab页
String homePath.path 首页页面路径(isTab=false时使用此字段)
Array.<String> homePath.tabs tab页面路径列表(isTab=true时使用自此段)

# eventDispatcher

EventDispatcher 类 事件监听器

点击查看源码
 /* eslint-disable require-jsdoc */
/**
 * @desc: EventDispatcher 类 事件监听器
 * @author: kexinwu@tencent.com
 */
/**
 * EventDispatcher 类 事件监听器
 * @class EventDispatcher
 * @classdesc 用于统一维护小程序的事件
 */
export default class EventDispatcher {
    constructor() {
        this.handlers = [];
    }
    /**
     * @memberof EventDispatcher
     * @param {String} type 事件名
     * @param {Function} handler 事件处理函数
     * @returns {Function} handler 事件处理函数
     */
    bind(type, handler) {
        if (typeof this.handlers[type] === 'undefined') {
            this.handlers[type] = [];
        }
        this.handlers[type].push(handler);
    }
    /**
     * @memberof EventDispatcher
     * @param       {String} type 事件名
     * @param       {Function} handler 事件处理函数
     * @returns     {Function} handler 事件处理函数
     */
    bindOnce(type, handler) {
        if (typeof this.handlers[type] === 'undefined') {
            this.handlers[type] = [];
        }
        const isBinded = this.handlers[type].length === 0;
        if (isBinded) {
            this.handlers[type].push(handler);
        }
    }
    /**
     * @memberof EventDispatcher
     * @param {String} type 事件名
     * @returns {Void} 无返回值
     */
    remove(type) {
        this.handlers[type] = [];
    }
    /**
     * @memberof EventDispatcher
     * @param       {String} type 事件名
     * @param       {Function} handler 事件处理函数
     * @returns {Void} 无返回值
     */
    unbind(type, handler) {
        if (this.handlers[type] instanceof Array) {
            const handlers = this.handlers[type];
            let i = 0;
            const l = handlers.length;
            for (; i < l; i += 1) {
                if (handlers[i] === handler) {
                    break;
                }
            }
            handlers.splice(i, 1);
        }
    }
    /**
     * @memberof EventDispatcher
     * @param  {Object} evt 事件描述
     * @param  {String} evt.type 事件类型
     * @returns     {Function} handler 事件处理函数
     */
    dispatch(evt) {
        if (this.handlers[evt.type] instanceof Array) {
            const handlers = this.handlers[evt.type];
            handlers.forEach(handler => handler(evt));
        }
    }
}
/**
 * @description 获取EventDispatcher的实例
 * @returns {Object} [EventDispatcher实例](#class-eventdispatcher)
 * @example
 * const { tms } = getApp({ allowDefault: true });
 * const events = tms.getEventDispatcher();
 * events.bind('xxx', () => {})
 * events.dispatch({type: 'xxx'})
 */
export const getEventDispatcher = () => new EventDispatcher();
 

# EventDispatcher

用于统一维护小程序的事件

# function bind

参数

字段名 类型 说明 默认值 是否可选
type String 事件名 ---
handler function 事件处理函数 ---

返回值

类型 说明
function handler 事件处理函数

# function bindOnce

参数

字段名 类型 说明 默认值 是否可选
type String 事件名 ---
handler function 事件处理函数 ---

返回值

类型 说明
function handler 事件处理函数

# Void remove

参数

字段名 类型 说明 默认值 是否可选
type String 事件名 ---

返回值

类型 说明
Void 无返回值

# Void unbind

参数

字段名 类型 说明 默认值 是否可选
type String 事件名 ---
handler function 事件处理函数 ---

返回值

类型 说明
Void 无返回值

# function dispatch

参数

字段名 类型 说明 默认值 是否可选
evt Object 事件描述 ---
evt.type String 事件类型 ---

返回值

类型 说明
function handler 事件处理函数

# Object getEventDispatcher

获取EventDispatcher的实例

返回值

类型 说明
Object EventDispatcher实例

示例代码

Example

const { tms } = getApp({ allowDefault: true });
const events = tms.getEventDispatcher();
events.bind('xxx', () => {})
events.dispatch({type: 'xxx'})

# funcUtils

点击查看源码
 /**
 * 函数节流
 * 用途:如果func触发频繁,限制一段时间内只能执行一次
 * @param func 执行函数
 * @param wait 要延迟的时间,单位:毫秒
 * @param immediate 首次调用或者超过wait时间间隔后调用时,是否先立即执行func
 */
function throttle(func, wait = 500, immediate = false) {
    let context = null;
    let args = null;
    let timer = 0;
    let last = 0;
    const run = () => {
        func.apply(context, args);
        timer = 0;
        context = null;
        args = null;
    };
    return function () {
        // @ts-ignore
        context = this;
        // eslint-disable-next-line
        args = arguments;
        if (timer) {
            return;
        }
        const t = last;
        last = Date.now();
        if (immediate && last - t >= wait) {
            run();
        }
        else {
            timer = setTimeout(run, wait);
        }
    };
}
/**
 * 函数防抖
 * 用途:如果func触发频繁,则在一段时间后没有再触发就执行一次
 * @param func 执行函数
 * @param wait 要延迟的时间,单位:毫秒
 * @param immediate 首次调用或者超过wait时间间隔后调用时,是否先立即执行func
 */
function debounce(func, wait = 500, immediate = false) {
    let args = null;
    let context = null;
    let timer = 0;
    let triggerTime = 0;
    const run = () => {
        const t = Date.now() - triggerTime - wait;
        if (t < 0) {
            timer = setTimeout(run, -t);
        }
        else {
            func.apply(context, args);
            timer = 0;
            args = null;
            context = null;
        }
    };
    return function () {
        // @ts-ignore
        context = this;
        // eslint-disable-next-line
        args = arguments;
        const t = triggerTime;
        triggerTime = Date.now();
        if (timer || (immediate && triggerTime - t < wait)) {
            return;
        }
        if (immediate) {
            func.apply(context, args);
            args = null;
            context = null;
        }
        else {
            timer = setTimeout(run, wait);
        }
    };
}
export { throttle, debounce, };
 

# throttle

函数节流 用途:如果func触发频繁,限制一段时间内只能执行一次

参数

字段名 类型 说明 默认值 是否可选
func 执行函数 ---
wait 要延迟的时间,单位:毫秒 ---
immediate 首次调用或者超过wait时间间隔后调用时,是否先立即执行func ---

# debounce

函数防抖 用途:如果func触发频繁,则在一段时间后没有再触发就执行一次

参数

字段名 类型 说明 默认值 是否可选
func 执行函数 ---
wait 要延迟的时间,单位:毫秒 ---
immediate 首次调用或者超过wait时间间隔后调用时,是否先立即执行func ---

# ipxHelper

iPhoneX相关交接接口

点击查看源码
 /**
 * @desc: iPhoneX相关交接接口
 * @author: petegao@tencent.com
 * Created: 2019-01-14.
 *
 */
import syncfnmanager from './syncfnmanager';
let isInit = false;
let isIPXSign = false;
let ipxClass = '';
/**
 * @private
 * @returns {Void}
 */
function init() {
    if (isInit) {
        return;
    }
    isInit = true;
    const sysInfo = syncfnmanager.getSystemInfoSync();
    const { model, platform: pl } = sysInfo;
    if (pl !== 'ios') {
        return;
    }
    if (model.search('iPhone X') > -1 || model.search('iPhone 11') > -1) {
        isIPXSign = true;
        ipxClass = 'view__ipx';
    }
}
/**
 * @description 判断是否是 iPhoneX
 * @returns {Boolean}
 */
function isIPX() {
    init();
    return isIPXSign;
}
/**
 * @description 返回 ipxClass
 * @returns {String}
 */
function getIpxClass() {
    init();
    return ipxClass;
}
/**
 * @param {Object} config 用户需要合并的IPX的配置
 * @returns {Object} 合并后的IPX的配置
 */
function getIpxConfig(config) {
    init();
    return {
        ...config,
        data: {
            ...config.data,
            isIPX: isIPXSign,
            ipxClass,
        },
    };
}
// 兼容旧接口,已经没什么用了
function ipxInit() {
}
export { ipxInit, isIPX, getIpxClass, getIpxConfig, };
 

# Boolean isIPX

判断是否是 iPhoneX

返回值

类型 说明
Boolean

# String getIpxClass

返回 ipxClass

返回值

类型 说明
String

# Object getIpxConfig

参数

字段名 类型 说明 默认值 是否可选
config Object 用户需要合并的IPX的配置 ---

返回值

类型 说明
Object 合并后的IPX的配置

# location

封装业务侧位置的接口。 将用户经纬度转化为城市等展示信息

点击查看源码
 /**
 * @module: location
 * @desc: 封装业务侧位置的接口。 将用户经纬度转化为城市等展示信息
 */
import Request from '../request';
import EventDispatcher from '../eventDispatcher';
import LocationBase from './base'; // eslint-disable-line
import { formatCityCode, formatPoi } from './utils'; // eslint-disable-line
const event = new EventDispatcher();
const userLocationCache = {}; // 获取位置缓存
// 用户手动选择的位置信息(切换城市后的位置信息)
let userLocation;
// 用户所在的城市
let cityTheUserAt = '';
// 用户城市变化事件名
const CityChangeEventName = 'loc_city_changed';
const LocationType = 'gcj02'; // 获取经纬度时的坐标名称
let ipLocationPromise = null; // ip定位请求的promise
/**
 * @class Location
 */
class Location extends LocationBase {
    /**
    * @desc 默认位置信息
    * @property {object} locForNoAuth 默认的位置信息
    * @property {string} locForNoAuth.privince 省份信息
    * @property {string} locForNoAuth.cityCode 城市编码
    * @property {string} locForNoAuth.cityName 城市名称
    * @property {string} locForNoAuth.district 地区名称
    * @property {string} locForNoAuth.latitude 维度
    * @property {string} locForNoAuth.longitude 经度
    * @example
    * const app = getApp({ allowDefault: true });
    * const locationManager = app.tms.getLocationManager(); // 获取位置管理器
    * console.log(locationManager.locForNoAuth) // 获取默认位置
    */
    locForNoAuth = {
        province: '广东省',
        cityCode: '440300',
        cityName: '深圳市',
        district: '福田区',
        latitude: 22.54286,
        longitude: 114.05956,
    };
    /**
     * 构造函数
     */
    constructor() {
        super();
    }
    /**
     * @description 根据经纬度获取地理位置信息, 比如城市信息
     * @param { number } lat 用户纬度信息
     * @param { number } lng 用户经度信息
     * @param { number } getPoi 位置  0 - 不获取(不返回poi字段), 1 - 获取(返回poi字段)
     * @returns { Promise<PostionType | void> } 获取位置信息的promise
     * @example
     * const app = getApp({ allowDefault: true });
     * const locationManager = app.tms.getLocationManager(); // 获取位置管理器
     * await locationManager.getPoiInfo('xxx', 'xxx', 1)
     */
    getPoiInfo(lat, lng, getPoi) {
        // 优先查询缓存中是否有位置信息,如果有直接返回
        const cacheName = `${lat}-${lng}-${getPoi}`;
        // @ts-ignore
        if (userLocationCache[cacheName]) { // eslint-disable-line
            return userLocationCache[cacheName];
        }
        userLocationCache[cacheName] = Request.getInstance().post('basic/lbs/decode', { lat, lng, getPoi })
            .then((res) => {
            const { errCode, resData = {} } = res;
            if (errCode === 0) {
                const { result = {} } = resData;
                const { ad_info: adInfo = {} } = result;
                const loc = {
                    nationCode: adInfo.nation_code,
                    province: adInfo.province,
                    cityName: adInfo.city,
                    district: adInfo.district,
                    adCode: adInfo.adcode,
                    cityCode: formatCityCode(adInfo.city_code, adInfo.nation_code),
                    latitude: lat,
                    longitude: lng,
                };
                if (getPoi === 0) {
                    return loc;
                }
                loc.adCode = adInfo.adcode;
                loc.poi = formatPoi(result);
                return loc;
            }
            return Promise.reject(res);
        })
            .catch(err => Promise.reject(err));
        return userLocationCache[cacheName];
    }
    /**
     * @function
     * @memberof Location
     * @description 获取位置、城市信息
     * @param       {Boolean} showModalWhenCloseAuth 获取位置时,如果用户关闭了位置授权,是否弹窗提示用户打开授权
     * @param       {String}  type 坐标类型
     * @param       {String}  content 弹窗内容
     * @param       {Number}  getPoi 是否获取详细poi信息, 0 - 不获取(不返回poi字段), 1 - 获取(返回poi字段)
     * @returns {Promise<POI|ERR>} 返回对象
     * @example
     * const app = getApp({ allowDefault: true });
     * const locationManager = app.tms.getLocationManager(); // 获取位置管理器
     * const res = await locationManager.getLocationDetail(false, 'gcj02', '', 1);
     * <caption>res-POI类型示例</caption>
     * {
     *   cityName: '北京市',
     *   cityCode: '100100',
     *   province: '北京市',
     *   latitude: 325.255333,
     *   longitude: 116.2545454,
     *   adCode: 1212144,
     *   poi: {
     *     id: '1114545554511',
     *     title: '腾讯北京总部大厦',
     *     address : '北京市海淀区东北旺西路',
     *   }
     * }
     * @example
     * <caption>res-ERR类型示例</caption>
     * {
     *   err,
     *   unAuthed: true,
     *   locationScopeStatus: false
     * }
     */
    getLocationDetail(showModalWhenCloseAuth = false, type = LocationType, content = '', getPoi = 0) {
        return this.getLocation(showModalWhenCloseAuth, type, content)
            .then(async (res) => {
            const res2 = await this.getPoiInfo(res.latitude, res.longitude, getPoi);
            // @ts-ignore
            res2.accuracy = res.accuracy;
            return res2;
        })
            .catch(err => Promise.reject(err));
    }
    /**
     * @description 以静默方式获取用户详细位置(经纬度,poi信息, 省市信息),说明如下:<br>
     * 1、从未授权情况下 | 用户删除小程序之后调用该方法不会弹微信自带的授权弹窗;<br>
     * 2、拒绝授权后,调用该方法不会弹窗提示用户去授权(wx.showModal);<br>
     * @memberof Location
     * @param {Number} getPoi 是否获取详细poi信息, 0 - 不获取(不返回poi字段), 1 - 获取(返回poi字段)
     * @param {String} type 非必填。坐标类型,默认'gcj02'
     * @returns {Promise<LOC|ERR>} 返回对象
     * @example
     * <caption>LOC类型示例</caption>
     * {
     *   cityName: '北京市',
     *   cityCode: '100100',
     *   latitude: 325.255333,
     *   longitude: 116.2545454,
     *   adCode: 1212144,
     *   poi: {
     *     id: '1114545554511',
     *     title: '腾讯北京总部大厦',
     *     address : '北京市海淀区东北旺西路',
     *   }
     * }
     * @example
     * <caption>ERR类型示例</caption>
     * {
     *   err,
     *   unAuthed: true,
     *   locationScopeStatus: false
     * }
     */
    getLocationDetailSilent(getPoi = 0, type = 'gcj02') {
        return this.getLocationSilent(type)
            .then(async (res) => {
            const res2 = await this.getPoiInfo(res.latitude, res.longitude, getPoi);
            // @ts-ignore
            res2.accuracy = res.accuracy;
            return res2;
        })
            .catch(err => Promise.reject(err));
    }
    /**
     * @description 获取用户手动选择的位置信息
     * @memberof Location
     * @returns {Null|LOC} 返回用户位置信息
     * @example
     * <caption>LOC类型示例</caption>
     * {
     *   province: '北京市',
     *   cityName: '北京市',
     *   cityCode: '100100',
     *   latitude: 325.255333,
     *   longitude: 116.2545454,
     *   adCode: 1212144,
     * }
    */
    getUserLocation() {
        return userLocation;
    }
    /**
     * @description 设置用户位置(小程序生命周期内有效)
     * @memberof Location
     * @param {Object} loc 用户手动选择的位置
     * @params {String} loc.province 省份
     * @params {String} loc.cityName 城市
     * @params {String} loc.cityCode citycode
     * @params {Number} loc.latitude 纬度
     * @params {Number} loc.longitude 经度
     * @params {Number} loc.adCode adCode
     * @returns {Void} 无返回值
     * @example
     * <caption>loc类型示例</caption>
     * {
     *   province: '北京市',
     *   cityName: '北京市',
     *   cityCode: '100100',
     *   latitude: 325.255333,
     *   longitude: 116.2545454,
     *   adCode: 1212144,
     * }
     */
    setUserLocation(loc) {
        if (!loc || typeof loc !== 'object') {
            return;
        }
        // 如果城市发生变化,派发事件
        const { cityName: curCityName } = loc;
        if (curCityName && (cityTheUserAt !== curCityName)) {
            cityTheUserAt = curCityName;
            userLocation = loc;
            event.dispatch({ type: CityChangeEventName });
        }
    }
    /**
     * @description 先获取用户手动设置的位置信息,获取不到则定位授权获取用户位置信息
     * @memberof Location
     * @param {Boolean} showModalWhenCloseAuth 没有授权时是否展示授权弹窗
     * @returns {Promise} 用户位置信息
     */
    async getMergedLocation(showModalWhenCloseAuth = true) {
        const userLocatioin = this.getUserLocation();
        // 优先用户选择的城市
        if (userLocatioin) {
            return userLocatioin;
        }
        return await this.getLocationDetail(showModalWhenCloseAuth, LocationType);
    }
    /**
     * @memberof Location
     * @description 监听用户城市变化(如: 北京市->深圳市)
     * @param       {Function} cb 监听事件回调函数
     * @returns {void}
     */
    onCityChange(cb) {
        if (cb && typeof cb === 'function') {
            event.bind(CityChangeEventName, cb);
        }
    }
    /**
     * @memberof Location
     * @description 获取路线规划
     * @param       {String} to 目的地坐标,格式:lat,lng
     * @param       {String} depart 出发地坐标,格式:lat,lng
     * @param       {String} mode 出行方式, driving 驾车[默认]
     * @returns     {Object} result.routes 返回数据 [{}, ...]
     */
    async getRoute(to, depart, mode = 'driving') {
        let from = depart;
        const coorReg = new RegExp(/(\d+\.\d{6,}),(\d+\.\d{6,})/);
        if (!coorReg.test(to))
            throw Error('目的地参数格式错误');
        if (!coorReg.test(from)) { // 出发地缺省使用当前位置
            const { latitude, longitude } = await this.getMergedLocation();
            from = `${latitude},${longitude}`;
        }
        return Request.getInstance().post('basic/lbs/direction', { from, to, mode });
    }
    /**
     * ip定位
     * 原理:通过调手图接口查询当前IP所在位置,市级的准确率是91%
     * 注意:由于服务端对该查询服务有次数限制,所以本函数会缓存成功的promise
     * @param force 是否清除上次的缓存重新请求
     */
    getIpLocation(force = false) {
        if (ipLocationPromise === null || force) {
            ipLocationPromise = new Promise((resolve, reject) => {
                Request.getInstance().post('basic/lbs/decodeip')
                    .then((res) => {
                    if (res.errCode === 0) {
                        resolve(res.resData);
                        return;
                    }
                    reject({ erMsg: res.errMsg });
                    ipLocationPromise = null;
                })
                    .catch((e) => {
                    reject(e);
                    ipLocationPromise = null;
                });
            });
        }
        return ipLocationPromise;
    }
}
// 因为在构造函数中会用到wx的api,所以使用到时才实例化
let instance;
function getLocInstance() {
    if (!instance) {
        instance = new Location();
    }
    return instance;
}
export default getLocInstance;
 

# Location

# locForNoAuth

默认位置信息

属性

字段名 类型 说明 默认值 是否可选
locForNoAuth object 默认的位置信息 ---
locForNoAuth.privince string 省份信息 ---
locForNoAuth.cityCode string 城市编码 ---
locForNoAuth.cityName string 城市名称 ---
locForNoAuth.district string 地区名称 ---
locForNoAuth.latitude string 维度 ---
locForNoAuth.longitude string 经度 ---

示例代码

Example

const app = getApp({ allowDefault: true });
const locationManager = app.tms.getLocationManager(); // 获取位置管理器
console.log(locationManager.locForNoAuth) // 获取默认位置

# Promise.<(PostionType|void)> getPoiInfo

根据经纬度获取地理位置信息, 比如城市信息

参数

字段名 类型 说明 默认值 是否可选
lat number 用户纬度信息 ---
lng number 用户经度信息 ---
getPoi number 位置 0 - 不获取(不返回poi字段), 1 - 获取(返回poi字段) ---

返回值

类型 说明
Promise.<(PostionType|void)> 获取位置信息的promise

示例代码

Example

const app = getApp({ allowDefault: true });
const locationManager = app.tms.getLocationManager(); // 获取位置管理器
await locationManager.getPoiInfo('xxx', 'xxx', 1)

# Promise.<(POI|ERR)> getLocationDetail

获取位置、城市信息

参数

字段名 类型 说明 默认值 是否可选
showModalWhenCloseAuth Boolean 获取位置时,如果用户关闭了位置授权,是否弹窗提示用户打开授权 ---
type String 坐标类型 ---
content String 弹窗内容 ---
getPoi Number 是否获取详细poi信息, 0 - 不获取(不返回poi字段), 1 - 获取(返回poi字段) ---

返回值

类型 说明
Promise.<(POI|ERR)> 返回对象

示例代码

Example (res-POI类型示例)

const app = getApp({ allowDefault: true });
const locationManager = app.tms.getLocationManager(); // 获取位置管理器
const res = await locationManager.getLocationDetail(false, 'gcj02', '', 1);
{
  cityName: '北京市',
  cityCode: '100100',
  province: '北京市',
  latitude: 325.255333,
  longitude: 116.2545454,
  adCode: 1212144,
  poi: {
    id: '1114545554511',
    title: '腾讯北京总部大厦',
    address : '北京市海淀区东北旺西路',
  }
}

Example (res-ERR类型示例)

{
  err,
  unAuthed: true,
  locationScopeStatus: false
}

# Promise.<(LOC|ERR)> getLocationDetailSilent

以静默方式获取用户详细位置(经纬度,poi信息, 省市信息),说明如下:<br> 1、从未授权情况下 | 用户删除小程序之后调用该方法不会弹微信自带的授权弹窗;<br> 2、拒绝授权后,调用该方法不会弹窗提示用户去授权(wx.showModal);<br>

参数

字段名 类型 说明 默认值 是否可选
getPoi Number 是否获取详细poi信息, 0 - 不获取(不返回poi字段), 1 - 获取(返回poi字段) ---
type String 非必填。坐标类型,默认'gcj02' ---

返回值

类型 说明
Promise.<(LOC|ERR)> 返回对象

示例代码

Example (LOC类型示例)

{
  cityName: '北京市',
  cityCode: '100100',
  latitude: 325.255333,
  longitude: 116.2545454,
  adCode: 1212144,
  poi: {
    id: '1114545554511',
    title: '腾讯北京总部大厦',
    address : '北京市海淀区东北旺西路',
  }
}

Example (ERR类型示例)

{
  err,
  unAuthed: true,
  locationScopeStatus: false
}

# NullLOC getUserLocation

获取用户手动选择的位置信息

返回值

类型 说明
Null|LOC 返回用户位置信息

示例代码

Example (LOC类型示例)

{
  province: '北京市',
  cityName: '北京市',
  cityCode: '100100',
  latitude: 325.255333,
  longitude: 116.2545454,
  adCode: 1212144,
}

# Void setUserLocation

设置用户位置(小程序生命周期内有效)

参数

字段名 类型 说明 默认值 是否可选
loc Object 用户手动选择的位置 ---

返回值

类型 说明
Void 无返回值

示例代码

Example (loc类型示例)

{
  province: '北京市',
  cityName: '北京市',
  cityCode: '100100',
  latitude: 325.255333,
  longitude: 116.2545454,
  adCode: 1212144,
}

# Promise getMergedLocation

先获取用户手动设置的位置信息,获取不到则定位授权获取用户位置信息

参数

字段名 类型 说明 默认值 是否可选
showModalWhenCloseAuth Boolean 没有授权时是否展示授权弹窗 ---

返回值

类型 说明
Promise 用户位置信息

# void onCityChange

监听用户城市变化(如: 北京市->深圳市)

参数

字段名 类型 说明 默认值 是否可选
cb function 监听事件回调函数 ---

返回值

类型 说明
void

# Object getRoute

获取路线规划

参数

字段名 类型 说明 默认值 是否可选
to String 目的地坐标,格式:lat,lng ---
depart String 出发地坐标,格式:lat,lng ---
mode String 出行方式, driving 驾车[默认] ---

返回值

类型 说明
Object result.routes 返回数据 [{}, ...]

# getIpLocation

ip定位 原理:通过调手图接口查询当前IP所在位置,市级的准确率是91% 注意:由于服务端对该查询服务有次数限制,所以本函数会缓存成功的promise

参数

字段名 类型 说明 默认值 是否可选
force 是否清除上次的缓存重新请求 ---

# log

本文件主要负责在小程序中日志打印功能,包含本地日志及实时日志.

点击查看源码
 /**
 * @desc: 本文件主要负责在小程序中日志打印功能,包含本地日志及实时日志.
 * 主要做了两件事:
 * 1、参数序列化处理;支持传递任意多个参数,并对类型为对象的参数进行字符串序列化处理(避免打印出来是'[Object Object]'的格式);
 * 2、低版本兼容;
 */
// 低版本不支持getLogManager或者getRealtimeLogManager时,用ManagerForLowerVersionLib来兼容
const ManagerForLowerVersionLib = {
    debug: () => { },
    info: () => { },
    log: () => { },
    warn: () => { },
    error: () => { },
    addFilterMsg: () => { },
    setFilterMsg: () => { },
};
// 小程序基础库2.7.1版本以上支持,所以需要兼容性处理
let logInstance = null;
let rtLogInstance = null;
/**
 * @private
 */
function getLogInstance() {
    if (logInstance === null) {
        logInstance = wx.getLogManager ? wx.getLogManager() : ManagerForLowerVersionLib;
    }
    return logInstance;
}
/**
 * @private
 */
function getRTLogInstance() {
    if (rtLogInstance === null) {
        rtLogInstance = wx.getRealtimeLogManager ? wx.getRealtimeLogManager() : ManagerForLowerVersionLib;
    }
    return rtLogInstance;
}
/**
 * @private
 * 参数中有对象类型的,将其转换为字符串类型,以便查看
 * @param {Array<Any>} params 需要格式化的数据
 * @returns {Array<String>} 字符串序列化后的数据
 */
const format = params => params.map(param => (typeof param === 'string' ? param : JSON.stringify(param)));
/**
 * @namespace LOG
 * @description 普通日志管理器,将日志记录在小程序日志文件中,用户上传后,可以在小程序后台-反馈管理中看到
 */
const LOG = {
    /**
     * @description 写debug日志
     * @param  {...Any} params 需要打印的数据,支持任意多个
     * @returns {Void} 无返回值
     */
    debug(...params) {
        getLogInstance().debug(...format(params));
    },
    /**
     * @description 写info日志
     * @param  {...Any} params 需要打印的数据,支持任意多个
     * @returns {Void} 无返回值
     */
    info(...params) {
        getLogInstance().info(...format(params));
    },
    /**
     * @description 写log日志
     * @param  {...Any} params 需要打印的数据,支持任意多个
     * @returns {Void} 无返回值
     */
    log(...params) {
        getLogInstance().log(...format(params));
    },
    /**
     * @description 写warn日志
     * @param  {...Any} params 需要打印的数据,支持任意多个
     * @returns {Void} 无返回值
     */
    warn(...params) {
        getLogInstance().warn(...format(params));
    },
    /**
     * @description 写warn日志. LogManager并没有error方法,为了兼容旧代码,所以声明一个error方法
     * @param  {...Any} params 需要打印的数据,支持任意多个
     * @returns {Void} 无返回值
     */
    error(...params) {
        LOG.warn(...params);
    },
};
/**
 * @namespace RTLOG
 * @description 实时日志,将日志实时上传至小程序后台-开发-运维中心-实时日志,方便快速排查漏洞,定位问题
 */
const RTLOG = {
    /**
     * @description 写info日志
     * @param  {...Any} params 需要打印的数据,支持任意多个
     * @returns {Void} 无返回值
     */
    info(...params) {
        getRTLogInstance().info(...format(params));
    },
    /**
     * @description 写warn日志
     * @param  {...Any} params 需要打印的数据,支持任意多个
     * @returns {Void} 无返回值
     */
    warn(...params) {
        getRTLogInstance().warn(...format(params));
    },
    /**
     * @description 写error日志
     * @param  {...Any} params 需要打印的数据,支持任意多个
     * @returns {Void} 无返回值
     */
    error(...params) {
        getRTLogInstance().error(...format(params));
    },
    /**
     * @description 添加过滤关键字
     * @param {String} msg 关键字
     * @returns {Void} 无返回值
     */
    addFilterMsg(msg) {
        getRTLogInstance().addFilterMsg(msg);
    },
    /**
     * @description 设置过滤关键字
     * @param {String} msg 关键字
     * @returns {Void} 无返回值
     */
    setFilterMsg(msg) {
        getRTLogInstance().setFilterMsg(msg);
    },
};
/**
 * @description 获取日志管理器对象,该对象提供的方法同wx.getLogManager()提供的方法,详见微信文档
 * @returns {Object} [LOG](#namespace-log)
 * @example
 * const { tms } = getApp({ allowDefault: true });
 * const logger = tms.getLogManager();
 * logger.log(1, 'str', { a: 1 }, ...);
 * logger.info(1, 'str', { a: 1 }, ...);
 * logger.debug(1, 'str', { a: 1 }, ...);
 * logger.awrn(1, 'str', { a: 1 }, ...);
 */
const getLogManager = () => LOG;
/**
 * @description 获取实时日志管理器对象,该对象提供的方法同wx.getRealtimeLogManager()提供的方法,详见微信文档
 * @returns {Object} [RTLOG](#namespace-rtlog)
 * @example
 * const { tms } = getApp({ allowDefault: true });
 * const logger = tms.getRealtimeLogManager();
 * logger.info(1, 'str', { a: 1 }, ...);
 * logger.warn(1, 'str', { a: 1 }, ...);
 * logger.error(1, 'str', { a: 1 }, ...);
 */
const getRealtimeLogManager = () => RTLOG;
export { getLogManager, getRealtimeLogManager, };
 

# namespace LOG

普通日志管理器,将日志记录在小程序日志文件中,用户上传后,可以在小程序后台-反馈管理中看到

# Void debug

写debug日志

参数

字段名 类型 说明 默认值 是否可选
params Any 需要打印的数据,支持任意多个 ---

返回值

类型 说明
Void 无返回值

# Void info

写info日志

参数

字段名 类型 说明 默认值 是否可选
params Any 需要打印的数据,支持任意多个 ---

返回值

类型 说明
Void 无返回值

# Void log

写log日志

参数

字段名 类型 说明 默认值 是否可选
params Any 需要打印的数据,支持任意多个 ---

返回值

类型 说明
Void 无返回值

# Void warn

写warn日志

参数

字段名 类型 说明 默认值 是否可选
params Any 需要打印的数据,支持任意多个 ---

返回值

类型 说明
Void 无返回值

# Void error

写warn日志. LogManager并没有error方法,为了兼容旧代码,所以声明一个error方法

参数

字段名 类型 说明 默认值 是否可选
params Any 需要打印的数据,支持任意多个 ---

返回值

类型 说明
Void 无返回值

# namespace RTLOG

实时日志,将日志实时上传至小程序后台-开发-运维中心-实时日志,方便快速排查漏洞,定位问题

# Void info

写info日志

参数

字段名 类型 说明 默认值 是否可选
params Any 需要打印的数据,支持任意多个 ---

返回值

类型 说明
Void 无返回值

# Void warn

写warn日志

参数

字段名 类型 说明 默认值 是否可选
params Any 需要打印的数据,支持任意多个 ---

返回值

类型 说明
Void 无返回值

# Void error

写error日志

参数

字段名 类型 说明 默认值 是否可选
params Any 需要打印的数据,支持任意多个 ---

返回值

类型 说明
Void 无返回值

# Void addFilterMsg

添加过滤关键字

参数

字段名 类型 说明 默认值 是否可选
msg String 关键字 ---

返回值

类型 说明
Void 无返回值

# Void setFilterMsg

设置过滤关键字

参数

字段名 类型 说明 默认值 是否可选
msg String 关键字 ---

返回值

类型 说明
Void 无返回值

# Object getLogManager

获取日志管理器对象,该对象提供的方法同wx.getLogManager()提供的方法,详见微信文档

返回值

类型 说明
Object LOG

示例代码

Example

const { tms } = getApp({ allowDefault: true });
const logger = tms.getLogManager();
logger.log(1, 'str', { a: 1 }, ...);
logger.info(1, 'str', { a: 1 }, ...);
logger.debug(1, 'str', { a: 1 }, ...);
logger.awrn(1, 'str', { a: 1 }, ...);

# Object getRealtimeLogManager

获取实时日志管理器对象,该对象提供的方法同wx.getRealtimeLogManager()提供的方法,详见微信文档

返回值

类型 说明
Object RTLOG

示例代码

Example

const { tms } = getApp({ allowDefault: true });
const logger = tms.getRealtimeLogManager();
logger.info(1, 'str', { a: 1 }, ...);
logger.warn(1, 'str', { a: 1 }, ...);
logger.error(1, 'str', { a: 1 }, ...);

# md5

点击查看源码
 /* eslint-disable no-param-reassign */
/**
 * @public
 * @description  基于md5算法对源字符串进行hash,生成hash字符串
 * @param {String} str 源字符串
 * @returns {String} 源字符串的md5 hash值
 * MIT https://github.com/blueimp/JavaScript-MD5
 */
/**
 * Add integers, wrapping at 2^32.
 * This uses 16-bit operations internally to work around bugs in interpreters.
 *
 * @param {number} x First integer
 * @param {number} y Second integer
 * @returns {number} Sum
 */
function safeAdd(x, y) {
    const lsw = (x & 0xffff) + (y & 0xffff);
    const msw = (x >> 16) + (y >> 16) + (lsw >> 16);
    return (msw << 16) | (lsw & 0xffff);
}
/**
 * Bitwise rotate a 32-bit number to the left.
 *
 * @param {number} num 32-bit number
 * @param {number} cnt Rotation count
 * @returns {number} Rotated number
 */
function bitRotateLeft(num, cnt) {
    return (num << cnt) | (num >>> (32 - cnt));
}
/**
 * Basic operation the algorithm uses.
 *
 * @param {number} q q
 * @param {number} a a
 * @param {number} b b
 * @param {number} x x
 * @param {number} s s
 * @param {number} t t
 * @returns {number} Result
 */
function md5cmn(q, a, b, x, s, t) {
    return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b);
}
/**
 * Basic operation the algorithm uses.
 *
 * @param {number} a a
 * @param {number} b b
 * @param {number} c c
 * @param {number} d d
 * @param {number} x x
 * @param {number} s s
 * @param {number} t t
 * @returns {number} Result
 */
function md5ff(a, b, c, d, x, s, t) {
    return md5cmn((b & c) | (~b & d), a, b, x, s, t);
}
/**
 * Basic operation the algorithm uses.
 *
 * @param {number} a a
 * @param {number} b b
 * @param {number} c c
 * @param {number} d d
 * @param {number} x x
 * @param {number} s s
 * @param {number} t t
 * @returns {number} Result
 */
function md5gg(a, b, c, d, x, s, t) {
    return md5cmn((b & d) | (c & ~d), a, b, x, s, t);
}
/**
 * Basic operation the algorithm uses.
 *
 * @param {number} a a
 * @param {number} b b
 * @param {number} c c
 * @param {number} d d
 * @param {number} x x
 * @param {number} s s
 * @param {number} t t
 * @returns {number} Result
 */
function md5hh(a, b, c, d, x, s, t) {
    return md5cmn(b ^ c ^ d, a, b, x, s, t);
}
/**
 * Basic operation the algorithm uses.
 *
 * @param {number} a a
 * @param {number} b b
 * @param {number} c c
 * @param {number} d d
 * @param {number} x x
 * @param {number} s s
 * @param {number} t t
 * @returns {number} Result
 */
function md5ii(a, b, c, d, x, s, t) {
    return md5cmn(c ^ (b | ~d), a, b, x, s, t);
}
/**
 * Calculate the MD5 of an array of little-endian words, and a bit length.
 *
 * @param {Array} x Array of little-endian words
 * @param {number} len Bit length
 * @returns {Array<number>} MD5 Array
 */
function binlMD5(x, len) {
    /* append padding */
    x[len >> 5] |= 0x80 << len % 32;
    x[(((len + 64) >>> 9) << 4) + 14] = len;
    let i;
    let olda;
    let oldb;
    let oldc;
    let oldd;
    let a = 1732584193;
    let b = -271733879;
    let c = -1732584194;
    let d = 271733878;
    for (i = 0; i < x.length; i += 16) {
        olda = a;
        oldb = b;
        oldc = c;
        oldd = d;
        a = md5ff(a, b, c, d, x[i], 7, -680876936);
        d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);
        c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);
        b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
        a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);
        d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
        c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
        b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);
        a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
        d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
        c = md5ff(c, d, a, b, x[i + 10], 17, -42063);
        b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
        a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
        d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);
        c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
        b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
        a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);
        d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
        c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);
        b = md5gg(b, c, d, a, x[i], 20, -373897302);
        a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);
        d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);
        c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);
        b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);
        a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);
        d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
        c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);
        b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
        a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
        d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);
        c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
        b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
        a = md5hh(a, b, c, d, x[i + 5], 4, -378558);
        d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
        c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
        b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);
        a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
        d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
        c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);
        b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
        a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);
        d = md5hh(d, a, b, c, x[i], 11, -358537222);
        c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);
        b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);
        a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);
        d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);
        c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);
        b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);
        a = md5ii(a, b, c, d, x[i], 6, -198630844);
        d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
        c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
        b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);
        a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
        d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
        c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);
        b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
        a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
        d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);
        c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
        b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
        a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);
        d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
        c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);
        b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);
        a = safeAdd(a, olda);
        b = safeAdd(b, oldb);
        c = safeAdd(c, oldc);
        d = safeAdd(d, oldd);
    }
    return [a, b, c, d];
}
/**
 * Convert an array of little-endian words to a string
 *
 * @param {Array<number>} input MD5 Array
 * @returns {string} MD5 string
 */
function binl2rstr(input) {
    let i;
    let output = '';
    const length32 = input.length * 32;
    for (i = 0; i < length32; i += 8) {
        output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff);
    }
    return output;
}
/**
 * Convert a raw string to an array of little-endian words
 * Characters >255 have their high-byte silently ignored.
 *
 * @param {string} input Raw input string
 * @returns {Array<number>} Array of little-endian words
 */
function rstr2binl(input) {
    let i;
    const output = [];
    output[(input.length >> 2) - 1] = undefined;
    for (i = 0; i < output.length; i += 1) {
        output[i] = 0;
    }
    const length8 = input.length * 8;
    for (i = 0; i < length8; i += 8) {
        output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32;
    }
    return output;
}
/**
 * Calculate the MD5 of a raw string
 *
 * @param {string} s Input string
 * @returns {string} Raw MD5 string
 */
function rstrMD5(s) {
    return binl2rstr(binlMD5(rstr2binl(s), s.length * 8));
}
/**
 * Calculates the HMAC-MD5 of a key and some data (raw strings)
 *
 * @param {string} key HMAC key
 * @param {string} data Raw input string
 * @returns {string} Raw MD5 string
 */
function rstrHMACMD5(key, data) {
    let i;
    let bkey = rstr2binl(key);
    const ipad = [];
    const opad = [];
    ipad[15] = undefined;
    opad[15] = undefined;
    if (bkey.length > 16) {
        bkey = binlMD5(bkey, key.length * 8);
    }
    for (i = 0; i < 16; i += 1) {
        ipad[i] = bkey[i] ^ 0x36363636;
        opad[i] = bkey[i] ^ 0x5c5c5c5c;
    }
    const hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
    return binl2rstr(binlMD5(opad.concat(hash), 512 + 128));
}
/**
 * Convert a raw string to a hex string
 *
 * @param {string} input Raw input string
 * @returns {string} Hex encoded string
 */
function rstr2hex(input) {
    const hexTab = '0123456789abcdef';
    let output = '';
    let x;
    let i;
    for (i = 0; i < input.length; i += 1) {
        x = input.charCodeAt(i);
        output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f);
    }
    return output;
}
/**
 * Encode a string as UTF-8
 *
 * @param {string} input Input string
 * @returns {string} UTF8 string
 */
function str2rstrUTF8(input) {
    return unescape(encodeURIComponent(input));
}
/**
 * Encodes input string as raw MD5 string
 *
 * @param {string} s Input string
 * @returns {string} Raw MD5 string
 */
function rawMD5(s) {
    return rstrMD5(str2rstrUTF8(s));
}
/**
 * Encodes input string as Hex encoded string
 *
 * @param {string} s Input string
 * @returns {string} Hex encoded string
 */
function hexMD5(s) {
    return rstr2hex(rawMD5(s));
}
/**
 * Calculates the raw HMAC-MD5 for the given key and data
 *
 * @param {string} k HMAC key
 * @param {string} d Input string
 * @returns {string} Raw MD5 string
 */
function rawHMACMD5(k, d) {
    return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d));
}
/**
 * Calculates the Hex encoded HMAC-MD5 for the given key and data
 *
 * @param {string} k HMAC key
 * @param {string} d Input string
 * @returns {string} Raw MD5 string
 */
function hexHMACMD5(k, d) {
    return rstr2hex(rawHMACMD5(k, d));
}
/**
 * Calculates MD5 value for a given string.
 * If a key is provided, calculates the HMAC-MD5 value.
 * Returns a Hex encoded string unless the raw argument is given.
 *
 * @param {string} string Input string
 * @param {string} [key] HMAC key
 * @param {boolean} [raw] Raw output switch
 * @returns {string} MD5 output
 */
function md5(string, key, raw) {
    if (!key) {
        if (!raw) {
            return hexMD5(string);
        }
        return rawMD5(string);
    }
    if (!raw) {
        return hexHMACMD5(key, string);
    }
    return rawHMACMD5(key, string);
}
export default md5;
 

# number safeAdd

Add integers, wrapping at 2^32. This uses 16-bit operations internally to work around bugs in interpreters.

参数

字段名 类型 说明 默认值 是否可选
x number First integer ---
y number Second integer ---

返回值

类型 说明
number Sum

# number bitRotateLeft

Bitwise rotate a 32-bit number to the left.

参数

字段名 类型 说明 默认值 是否可选
num number 32-bit number ---
cnt number Rotation count ---

返回值

类型 说明
number Rotated number

# number md5cmn

Basic operation the algorithm uses.

参数

字段名 类型 说明 默认值 是否可选
q number q ---
a number a ---
b number b ---
x number x ---
s number s ---
t number t ---

返回值

类型 说明
number Result

# number md5ff

Basic operation the algorithm uses.

参数

字段名 类型 说明 默认值 是否可选
a number a ---
b number b ---
c number c ---
d number d ---
x number x ---
s number s ---
t number t ---

返回值

类型 说明
number Result

# number md5gg

Basic operation the algorithm uses.

参数

字段名 类型 说明 默认值 是否可选
a number a ---
b number b ---
c number c ---
d number d ---
x number x ---
s number s ---
t number t ---

返回值

类型 说明
number Result

# number md5hh

Basic operation the algorithm uses.

参数

字段名 类型 说明 默认值 是否可选
a number a ---
b number b ---
c number c ---
d number d ---
x number x ---
s number s ---
t number t ---

返回值

类型 说明
number Result

# number md5ii

Basic operation the algorithm uses.

参数

字段名 类型 说明 默认值 是否可选
a number a ---
b number b ---
c number c ---
d number d ---
x number x ---
s number s ---
t number t ---

返回值

类型 说明
number Result

# Array.<number> binlMD5

Calculate the MD5 of an array of little-endian words, and a bit length.

参数

字段名 类型 说明 默认值 是否可选
x Array Array of little-endian words ---
len number Bit length ---

返回值

类型 说明
Array.<number> MD5 Array

# string binl2rstr

Convert an array of little-endian words to a string

参数

字段名 类型 说明 默认值 是否可选
input Array.<number> MD5 Array ---

返回值

类型 说明
string MD5 string

# Array.<number> rstr2binl

Convert a raw string to an array of little-endian words Characters >255 have their high-byte silently ignored.

参数

字段名 类型 说明 默认值 是否可选
input string Raw input string ---

返回值

类型 说明
Array.<number> Array of little-endian words

# string rstrMD5

Calculate the MD5 of a raw string

参数

字段名 类型 说明 默认值 是否可选
s string Input string ---

返回值

类型 说明
string Raw MD5 string

# string rstrHMACMD5

Calculates the HMAC-MD5 of a key and some data (raw strings)

参数

字段名 类型 说明 默认值 是否可选
key string HMAC key ---
data string Raw input string ---

返回值

类型 说明
string Raw MD5 string

# string rstr2hex

Convert a raw string to a hex string

参数

字段名 类型 说明 默认值 是否可选
input string Raw input string ---

返回值

类型 说明
string Hex encoded string

# string str2rstrUTF8

Encode a string as UTF-8

参数

字段名 类型 说明 默认值 是否可选
input string Input string ---

返回值

类型 说明
string UTF8 string

# string rawMD5

Encodes input string as raw MD5 string

参数

字段名 类型 说明 默认值 是否可选
s string Input string ---

返回值

类型 说明
string Raw MD5 string

# string hexMD5

Encodes input string as Hex encoded string

参数

字段名 类型 说明 默认值 是否可选
s string Input string ---

返回值

类型 说明
string Hex encoded string

# string rawHMACMD5

Calculates the raw HMAC-MD5 for the given key and data

参数

字段名 类型 说明 默认值 是否可选
k string HMAC key ---
d string Input string ---

返回值

类型 说明
string Raw MD5 string

# string hexHMACMD5

Calculates the Hex encoded HMAC-MD5 for the given key and data

参数

字段名 类型 说明 默认值 是否可选
k string HMAC key ---
d string Input string ---

返回值

类型 说明
string Raw MD5 string

# string md5

Calculates MD5 value for a given string. If a key is provided, calculates the HMAC-MD5 value. Returns a Hex encoded string unless the raw argument is given.

参数

字段名 类型 说明 默认值 是否可选
string string Input string ---
key string HMAC key ---
raw boolean Raw output switch ---

返回值

类型 说明
string MD5 output

# mpInfo

支持服务接入相关接口

点击查看源码
 /**
 * @desc: 支持服务接入相关接口
 */
import Request from './request';
/**
 * getMpOpenId 获取接入方用户唯一标识 [变更为 getOuterOpenId]
 * @private
 * @description 唯一标识 openId 用于与服务商建立账号关联关系
 * @category 服务接入
 * @param {String} mpId 接入服务商渠道标识
 * @param {String} userId 出行用户标识
 * @returns {Promise<String>} 返回 openId,失败时返回空
 * @example
 * const { tms } = getApp({ allowDefault: true });
 * tms.getMpOpenId('xxx', '111')
 */
async function getMpOpenId(mpId, userId) {
    const { resData } = await Request.getInstance().post('user/mpinfo', { userId, mpId });
    const { openId = '' } = resData || {};
    return openId;
}
/**
 * getOuterOpenId 获取接入方用户唯一标识
 * @public
 * @description 唯一标识 openId 用于服务接入方作为唯一标识、向腾讯出行服务同步订单等
 * @category 服务接入
 * @param {String} apiKey 服务接入方渠道标识
 * @returns {Promise<String>} 返回 openId,失败时返回空
 * @example
 * const { tms } = getApp({ allowDefault: true });
 * tms.getMpOpenId('xxx', '111')
 */
async function getOuterOpenId(apiKey) {
    const { resData } = await Request.getInstance().post('user/mpinfo', { mpId: apiKey });
    const { openId = '' } = resData || {};
    return openId;
}
/**
 * 获取用户加密数据
 * @param {String} mpId        服务接入方渠道标识,spId 或 apiKey。注意:如果使用 apiKey,需要在后台增加 apiKey 和 spId 的映射
 * @param {String} queryTypes  要加密的用户信息类型集合,多个种类型用','连接;支持的类型见下文“用户信息类型标识” |
 * @param {Object} propNameMap 加密数据对象中信息字段名映射
 * @returns {Promise<{ encrStr: String }>} 加密字符串
 */
async function getEncryptUserInfo(mpId = '', queryTypes = '', propNameMap = {}) {
    if (!mpId)
        return Promise.reject('缺少服务标志mpId');
    if (!queryTypes)
        return Promise.reject('缺少需要加密的用户信息字段描述');
    return new Request().get('user/info/encrypt', { mpId, queryTypes, propNameMap })
        .then((res) => {
        const { resData, errCode, errMsg } = res || {};
        if (errCode === 0) {
            // 请求成功
            return resData;
        }
        return Promise.reject(errMsg);
    })
        .catch(() => Promise.reject('请求发生错误,请重试'));
}
export { getMpOpenId, getOuterOpenId, getEncryptUserInfo, };
 

# Promise.<{encrStr: String}> getEncryptUserInfo

获取用户加密数据

参数

字段名 类型 说明 默认值 是否可选
mpId String 服务接入方渠道标识,spId 或 apiKey。注意:如果使用 apiKey,需要在后台增加 apiKey 和 spId 的映射 ---
queryTypes String 要加密的用户信息类型集合,多个种类型用','连接;支持的类型见下文“用户信息类型标识” ---
propNameMap Object 加密数据对象中信息字段名映射 ---

返回值

类型 说明
Promise.<{encrStr: String}> 加密字符串

# Promise.<String> getOuterOpenId

唯一标识 openId 用于服务接入方作为唯一标识、向腾讯出行服务同步订单等

服务接入

参数

字段名 类型 说明 默认值 是否可选
apiKey String 服务接入方渠道标识 ---

返回值

类型 说明
Promise.<String> 返回 openId,失败时返回空

示例代码

Example

const { tms } = getApp({ allowDefault: true });
tms.getMpOpenId('xxx', '111')

获取导航高度的相关函数

点击查看源码
 /**
 * @desc: 获取导航高度的相关函数
 */
import { compareVersion } from './compareVersion';
let systemInfo = null;
/**
 * 获取系统信息,有缓存, 返回数据同wx.getSystemInfoSync  {@link https://developers.weixin.qq.com/miniprogram/dev/api/base/system/wx.getSystemInfoSync.html}
 * @returns {Object} 系统信息
 */
const getSystemInfoSync = () => {
    if (systemInfo)
        return systemInfo;
    let res;
    try {
        systemInfo = wx.getSystemInfoSync();
        res = systemInfo;
    }
    catch (_) {
        res = {};
    }
    return res;
};
/**
 * 获取系统信息,有缓存, 返回数据同wx.getSystemInfoSync {@link https://developers.weixin.qq.com/miniprogram/dev/api/base/system/wx.getSystemInfoSync.html}
 * @returns {Object} 系统信息
 */
const getSysInfo = () => {
    let sysInfo = {};
    try {
        sysInfo = getSystemInfoSync();
        sysInfo.getSystemInfoSuccess = true;
    }
    catch (e) {
        // 获取系统信息失败时,使用默认数据
        sysInfo = { getSystemInfoSuccess: false, statusBarHeight: 0, windowWidth: 0 };
    }
    return sysInfo;
};
/**
 * 获取胶囊位置信息,返回数据同wx.getMenuButtonBoundingClientRect {@link https://developers.weixin.qq.com/miniprogram/dev/api/ui/menu/wx.getMenuButtonBoundingClientRect.html}
 * @returns {Object} 胶囊位置信息
 */
const getMenuButtonRectInfo = () => {
    let menuButtonRectInfo = {};
    try {
        menuButtonRectInfo = wx.getMenuButtonBoundingClientRect();
        menuButtonRectInfo.getMenuRectInfoSuccess = true;
    }
    catch (e) {
        // 获取胶囊位置信息失败,使用默认值
        menuButtonRectInfo = { getMenuRectInfoSuccess: false };
    }
    return menuButtonRectInfo;
};
/**
 * 胶囊高度适配,以兼容获取到的胶囊高度值非法的情况
 * @private
 * @param {Number} height 胶囊高度
 * @param {Boolean} isIOS 是否是ios系统
 * @returns {Number} 胶囊高度
 */
const formatMenuHeight = (height, isIOS) => {
    if (height > 0) {
        return height;
    }
    return isIOS ? 32 : 34;
};
/**
 * 计算自定义导航栏布局信息
 * @private
 * @param {Boolean} isIOS          是否是ios平台
 * @param {Number} statusBarHeight 状态栏高度
 * @param {String} apiCategory     API类别
 * @returns {Object} data                 导航栏布局信息
 * @returns {Number} data.statusBarHeight 状态栏高度,单位rpx
 * @returns {Number} data.navBarHeight    导航栏高度(不含状态栏),单位rpx
 * @returns {Number} data.menuTop         菜单按钮上边距,单位rpx
 * @returns {Number} data.menuRight       菜单按钮右边缘距离屏幕左边缘的距离,单位rpx
 * @returns {Number} data.menuWidth       菜单按钮宽度,单位rpx
 * @returns {Number} data.menuHeight      菜单按钮高度,单位rpx
 */
const calculateNavBarLayout = (isIOS, statusBarHeight, apiCategory) => {
    const data = {};
    const menuTopOnIos = statusBarHeight + 4;
    const menuTopOnAndroid = statusBarHeight + 8; // statusBarHeight高度,不同机型差距较大,不适合写死
    const menuRectInfo = getMenuButtonRectInfo(); // 插件下基础库2.15.0起支持
    let { right, width, height = 0, top } = menuRectInfo;
    // 数据有异常时使用默认值
    if (top <= 0) {
        top = isIOS ? menuTopOnIos : menuTopOnAndroid;
    }
    if (right <= 0) {
        right = isIOS ? 368 : 383;
    }
    if (width < 0) {
        width = isIOS ? 87 : 94;
    }
    height = formatMenuHeight(height, isIOS);
    data.statusBarHeight = calStatusBarHeight(statusBarHeight, apiCategory); // 顶部状态栏高度
    data.navBarHeight = calNavBarHeight(top, height, statusBarHeight, apiCategory); // 小程序导航栏高度
    data.menuTop = top; // 菜单按钮上边距
    data.menuRight = right; // 菜单按钮右边距离屏幕左边缘的距离
    data.menuWidth = width; // 菜单按钮宽度
    data.menuHeight = height; // 菜单按钮高度
    return data;
};
const calStatusBarHeight = (statusBarHeight, apiCategory) => {
    if (apiCategory !== 'embedded')
        return statusBarHeight;
    return 0;
};
const calNavBarHeight = (menuTop, menuHeight, statusBarHeight, apiCategory) => {
    if (apiCategory !== 'embedded') {
        return menuHeight + ((menuTop - statusBarHeight) * 2);
    }
    return menuHeight + (7 * 2);
};
/**
 * 获取导航栏相关信息
 * @returns {Object} data                 导航栏布局信息
 * @returns {Number} data.enable          是否支持自定义导航栏
 * @returns {Number} data.statusBarHeight 状态栏高度,单位rpx
 * @returns {Number} data.navBarHeight    导航栏高度(不含状态栏),单位rpx
 * @returns {Number} data.menuTop         菜单按钮上边距,单位rpx
 * @returns {Number} data.menuRight       菜单按钮右边缘距离屏幕左边缘的距离,单位rpx
 * @returns {Number} data.menuWidth       菜单按钮宽度,单位rpx
 * @returns {Number} data.menuHeight      菜单按钮高度,单位rpx
 * @returns {Object} data.enterOptions             启动参数
 * @returns {Object} data.enterOptions.apiCategory API 类别(不同apiCategory场景下的API有不同限制,UI也有不同)
 */
const getNavBarConfigData = () => {
    const systemInfo = getSysInfo();
    const { brand, getSystemInfoSuccess, platform } = systemInfo;
    const isIOS = platform === 'ios';
    const StatusBarHeightOnIOS = 44;
    const StatusBarHeightOnAndroid = 24;
    let { statusBarHeight, version } = systemInfo;
    // 获取系统信息失败,给设置一个默认版本
    if (!getSystemInfoSuccess || !version) {
        version = '7.0.1';
    }
    // 获取状态栏高度失败 | 接口调用成功,但数据为0时,使用默认值
    if (!getSystemInfoSuccess || (getSystemInfoSuccess && statusBarHeight <= 0)) {
        statusBarHeight = isIOS ? StatusBarHeightOnIOS : StatusBarHeightOnAndroid;
    }
    const enable = compareVersion(version, '7.0.0') >= 0 || brand === 'devtools'; // 微信版本是否支持自定义顶栏,不支持时自动隐藏
    const enterOptions = getEnterOptions();
    if (enable) {
        return { enable, ...calculateNavBarLayout(isIOS, statusBarHeight, enterOptions.apiCategory) };
    }
    return { enable };
};
// 获取需要对外暴露的启动参数
const getEnterOptions = () => {
    const options = wx.getEnterOptionsSync();
    const { apiCategory = 'default' } = options || {};
    return { apiCategory };
};
export { getNavBarConfigData, };
 

# Object getSystemInfoSync

获取系统信息,有缓存, 返回数据同wx.getSystemInfoSync link (opens new window)

返回值

类型 说明
Object 系统信息

# Object getSysInfo

获取系统信息,有缓存, 返回数据同wx.getSystemInfoSync link (opens new window)

返回值

类型 说明
Object 系统信息

# Object getMenuButtonRectInfo

获取胶囊位置信息,返回数据同wx.getMenuButtonBoundingClientRect link (opens new window)

返回值

类型 说明
Object 胶囊位置信息

# Object getNavBarConfigData

获取导航栏相关信息

返回值

类型 说明
Object data 导航栏布局信息
Number data.enable 是否支持自定义导航栏
Number data.statusBarHeight 状态栏高度,单位rpx
Number data.navBarHeight 导航栏高度(不含状态栏),单位rpx
Number data.menuTop 菜单按钮上边距,单位rpx
Number data.menuRight 菜单按钮右边缘距离屏幕左边缘的距离,单位rpx
Number data.menuWidth 菜单按钮宽度,单位rpx
Number data.menuHeight 菜单按钮高度,单位rpx
Object data.enterOptions 启动参数
Object data.enterOptions.apiCategory API 类别(不同apiCategory场景下的API有不同限制,UI也有不同)

小程序跳转相关方法

点击查看源码
 /**
 * @desc: 小程序跳转相关方法
 * @author Davislu <davislu@tencent.com>
 */
/**
 * DEFN 空方法
 * @private
 * @returns {undefined} 无返回值
 */
const DEFN = () => { };
/**
 * navigateToWebview 方法 跳转到小程序的 web-view 容器打开 H5
 * @param {object} setting 配置信息
 * @param {string} setting.url 需要跳转的 H5 连接
 * @param {function} setting.complete 跳转成功后的回调函数
 * @param {function} setting.message 用于获取 H5 中的 postMessage 的数据
 * @param {object} setting.share 页面分享信息
 * @param {string} setting.share.title 页面分享标题
 * @param {string} setting.share.image 页面分享图片
 * @param {string} setting.share.disable 是否禁用页面分享
 * @param {object} setting.navbar 页面导航栏设置
 * @param {string} setting.navbar.frontColor 导航栏字体颜色
 * @param {string} setting.navbar.backgroundColor 导航栏背景颜色
 * @returns {void} 无返回值
*/
const navigateToWebview = ({ url: webUrl, complete = DEFN, message = DEFN, share = {}, navbar = {} }) => {
    const page = '/modules/x/webcontainer/webcontainer';
    let query = `url=${encodeURIComponent(webUrl)}`;
    if (share.disable) {
        query += '&disableShare=true';
    }
    else if (share.title) {
        const image = share.image ? `&image=${encodeURIComponent(share.image)}` : '';
        query += `&title=${encodeURIComponent(share.title)}${image}`;
    }
    if (navbar.frontColor)
        query += `&navbarFront=${navbar.frontColor}`;
    if (navbar.backgroundColor)
        query += `&navbarBg=${navbar.backgroundColor}`;
    const url = `${page}${/\?/.test(page) ? '&' : '?'}${query}`;
    const navSetting = { url, complete };
    navSetting.events = { onMessage: message };
    wx.navigateTo(navSetting);
};
const obj = {
    navigateToWebview,
};
export default obj;
 

# void navigateToWebview

navigateToWebview 方法 跳转到小程序的 web-view 容器打开 H5

参数

字段名 类型 说明 默认值 是否可选
setting object 配置信息 ---
setting.url string 需要跳转的 H5 连接 ---
setting.complete function 跳转成功后的回调函数 ---
setting.message function 用于获取 H5 中的 postMessage 的数据 ---
setting.share object 页面分享信息 ---
setting.share.title string 页面分享标题 ---
setting.share.image string 页面分享图片 ---
setting.share.disable string 是否禁用页面分享 ---
setting.navbar object 页面导航栏设置 ---
setting.navbar.frontColor string 导航栏字体颜色 ---
setting.navbar.backgroundColor string 导航栏背景颜色 ---

返回值

类型 说明
void 无返回值

# numUtils

数字处理相关函数

点击查看源码
 /**
 * @desc: 数字处理相关函数
 */
import { roundStr } from './stringUtils';
/**
 * 四舍五入(支持保留n位小数,n>=0)
 * @param {any} x 原数字, 如果n不是合法数字或者无法转换为合法数字,round结果返回NaN
 * @param {any} n 保留几位小数,默认0;
 1. 如果n不是合法数字或者无法转换为合法数字,round结果返回NaN;
 2. 如果n小于0,round结果返回NaN;
 3. 如果n的值包含小数部分,round处理时只关注n的整数部分值
 * @return {number} 返回一个保留n位小数的数字,异常情况下可能是NaN
 */
const round = (x, n = 0) => parseFloat(roundStr(x, n, false));
export { round, };
 

# number round

四舍五入(支持保留n位小数,n>=0)

参数

字段名 类型 说明 默认值 是否可选
x any 原数字, 如果n不是合法数字或者无法转换为合法数字,round结果返回NaN ---
n any 保留几位小数,默认0;
  1. 如果n不是合法数字或者无法转换为合法数字,round结果返回NaN;
  2. 如果n小于0,round结果返回NaN;
  3. 如果n的值包含小数部分,round处理时只关注n的整数部分值 | --- | 否 |

返回值

类型 说明
number 返回一个保留n位小数的数字,异常情况下可能是NaN

# objUtils

对象处理相关函数

点击查看源码
 /**
 * @desc: 对象处理相关函数
 */
/**
 * @function
 * @description 把对象拼接成 a=b&c=d 形式的字符串
 * @param       {Object} queryObj  需要进行序列化的对象
 * @returns     {String} 拼接后的字符串
 */
export const serialize = (queryObj = {}) => {
    if (!queryObj) {
        return '';
    }
    const queryArray = [];
    Object.keys(queryObj).forEach((key) => {
        queryArray.push(`${key}=${queryObj[key]}`);
    });
    return queryArray.join('&');
};
export class JsonParseError extends Error {
    constructor(text, data) {
        super(text);
        this.data = data;
    }
}
/**
 * 安全的JSON.parse
 * @param {object} data JSON.parse的对象
 * @param {boolean} throwErrIfParseFail 如果解析失败,是否抛出错误
 */
export function safeJsonParse(data, throwErrIfParseFail = false) {
    try {
        return JSON.parse(data);
    }
    catch (e) {
        if (throwErrIfParseFail) {
            throw new JsonParseError('JSON.parse error', data);
        }
    }
    return data;
}
 

# String serialize

把对象拼接成 a=b&c=d 形式的字符串

参数

字段名 类型 说明 默认值 是否可选
queryObj Object 需要进行序列化的对象 ---

返回值

类型 说明
String 拼接后的字符串

# safeJsonParse

安全的JSON.parse

参数

字段名 类型 说明 默认值 是否可选
data object JSON.parse的对象 ---
throwErrIfParseFail boolean 如果解析失败,是否抛出错误 ---

# report

埋点上报

点击查看源码
 /**
 * @desc: 埋点上报
 * @module: report
 */
// / <reference path='./types.ts'/>
import helper from './helper';
import sender from './sender';
import formatV1 from './formatV1';
import formatV2 from './formatV2';
const logger = wx.getLogManager({});
logger.log('report模块代码被装载');
/**
 * 初始化
 * @private
 */
function init(options) {
    helper.init(options);
}
/**
 * 旧埋点
 * @param { object } data 上报对象
 * @example
 * report({ 27: xxxx, 34: xxx })
 */
function report(data = {}) {
    if (helper.canReport()) {
        logger.log('旧埋点:', data);
        formatV1.formatData(data).then(arr => sender.queue(arr));
    }
}
/**
 * 旧埋点,快速上报,不依赖用户位置
 * @param { object } data 上报对象
 * @example
 * fastReport({ 27: xxxx, 34: xxx })
 */
function fastReport(data = {}) {
    if (helper.canReport()) {
        logger.log('旧立即埋点:', data);
        const arr = formatV1.formatFastData(data);
        sender.send(arr);
    }
}
/**
 * 新埋点
 * @param {string} 参数1 页面|组件的唯一标志
 * @param {any}  参数2 埋点属性(会埋在埋点31个字段)
 * @param {any} 参数3 埋点属性(会埋在埋点32个字段) 依次类推参数4、参数5,最多10个参数
 * @returns {void}
 * @example
 * const { tms } = getApp({ allowDefault: true });
 * const reporter = tms.getReporter();
 * reporter.report2('user_security_level', {}, {});
 */
function report2(...data) {
    if (helper.canReport()) {
        logger.log('新埋点:', data);
        formatV2.formatData(data).then(arr => sender.queue(arr));
    }
}
/**
 * 新埋点,埋点需要立即上报,不依赖用户位置,
 * fastReport2()上报时携带的"省"、"市"等需要异步请求的基础字段会从缓存中读取,如果无缓存则为空,report2()则一定会携带这些字段
 * @param {string} 参数1 页面|组件的唯一标志
 * @param {any}  参数2 埋点属性(会埋在埋点31个字段)
 * @param {any} 参数3 埋点属性(会埋在埋点32个字段) 依次类推参数4、参数5,最多10个参数
 * @returns {void}
 * @example
 * const { tms } = getApp({ allowDefault: true });
 * const reporter = tms.getReporter();
 * reporter.fastReport2('user_security_level', {}, {});
 */
function fastReport2(...data) {
    if (helper.canReport()) {
        logger.log('新立即埋点:', data);
        const arr = formatV2.formatFastData(data);
        sender.send(arr);
    }
}
export default {
    init,
    report,
    fastReport,
    report2,
    fastReport2,
    setSystemField: helper.setSystemField,
};
 

# report

旧埋点

参数

字段名 类型 说明 默认值 是否可选
data object 上报对象 ---

示例代码

Example

report({ 27: xxxx, 34: xxx })

# fastReport

旧埋点,快速上报,不依赖用户位置

参数

字段名 类型 说明 默认值 是否可选
data object 上报对象 ---

示例代码

Example

fastReport({ 27: xxxx, 34: xxx })

# void report2

新埋点

参数

字段名 类型 说明 默认值 是否可选
参数1 string 页面 组件的唯一标志 ---
参数2 any 埋点属性(会埋在埋点31个字段) ---
参数3 any 埋点属性(会埋在埋点32个字段) 依次类推参数4、参数5,最多10个参数 ---

返回值

类型 说明
void

示例代码

Example

const { tms } = getApp({ allowDefault: true });
const reporter = tms.getReporter();
reporter.report2('user_security_level', {}, {});

# void fastReport2

新埋点,埋点需要立即上报,不依赖用户位置, fastReport2()上报时携带的"省"、"市"等需要异步请求的基础字段会从缓存中读取,如果无缓存则为空,report2()则一定会携带这些字段

参数

字段名 类型 说明 默认值 是否可选
参数1 string 页面 组件的唯一标志 ---
参数2 any 埋点属性(会埋在埋点31个字段) ---
参数3 any 埋点属性(会埋在埋点32个字段) 依次类推参数4、参数5,最多10个参数 ---

返回值

类型 说明
void

示例代码

Example

const { tms } = getApp({ allowDefault: true });
const reporter = tms.getReporter();
reporter.fastReport2('user_security_level', {}, {});

# request

request模块作为基于 tms-core 的应用的公共请求模块。

点击查看源码
 /**
 * @copyright 2021-present, Tencent, Inc. All rights reserved.
 * @brief request.js用于发起网络请求.
 * @desc: request模块作为基于 tms-core 的应用的公共请求模块。
 * 目前支持在出行服务小程序或基于出行服务的小程序中调用。在后续runtime支持公众号H5后,
 * 将支持在H5中调用。
 *
 * 考虑到对不同运行环境的支持,强依赖运行环境的依赖,比如 wx.request,应通过注入的形式提供。
 * 框架判断在不同的运行环境,切换调用不同运行环境提供的方法。
 */
import md5 from './md5';
import { getEnvInfo, getAuthInfo } from './env';
import { safeJsonParse } from './objUtils';
const logger = wx.getLogManager({});
/**
 * 用于序列化需要签名的参数
 * @private
 * @param {object} param 需要序列化的参数
 * @returns {string} 序列化之后的参数字符串
 */
const seriesParam = (param) => {
    const keys = Object.keys(param)
        .sort();
    const series = keys.map((key) => {
        const val = param[key];
        return `${key}${typeof val === 'object' ? JSON.stringify(val) : val}`;
    });
    return series.join('');
};
/**
 * 用于对request请求对象做签名
 * @private
 * @param {object} param 需要做签名的参数
 * @param {string} secretKey 签名密钥
 * @returns {object} 签名后的参数对象
 */
const sign = (param = {}, secretKey) => {
    const signture = md5(seriesParam(param) + secretKey);
    return { ...param, sign: signture };
};
/**
 * 用于对request请求对象添加系统参数
 * @private
 * @param {object} param 接口调用传入的参数
 * @param {Boolean} withAuth 是否需要登录参数
 * @param {object} baseParam request实例定义的基础参数
 * @returns {object} 全部参数对象
 */
const composeParam = async (param = {}, withAuth = true, baseParam = {}) => {
    const version = '1.0';
    const { appVersion, wxAppId, client } = getEnvInfo();
    const nonce = Math.random()
        .toString(36)
        .substr(2, 10);
    const timestamp = Date.now();
    const random = Math.random()
        .toString()
        .slice(2, 7);
    const sourceId = ['', 'sinan', 'mycar'].indexOf(client) + 7; // 6 未知 7 云函数 8 出行 9 我的车
    const seqId = `${timestamp}${sourceId}${random}`;
    const paramsWithAuth = await modifyAuthParam(param, withAuth);
    const combinedParam = Object.assign({
        version,
        appVersion,
        nonce,
        timestamp,
        seqId,
        wxAppId,
    }, { ...baseParam }, { ...paramsWithAuth });
    // 清理undefined和NaN的参数
    Object.keys(combinedParam)
        .forEach((key) => {
        if (typeof combinedParam[key] === 'number' && isNaN(combinedParam[key])) {
            delete combinedParam[key];
        }
        if (typeof combinedParam[key] === 'undefined') {
            delete combinedParam[key];
        }
    });
    return combinedParam;
};
/**
 * 用于保证业务参数的登录态参数,
 * 若接口不依赖登录态 如 user/login,则保证参数中不包括userId & token,
 * 若接口依赖登录态,则保证参数中填充userId & token,
 * @private
 * @param {object} param 要校验登录态的业务参数
 * @param {boolean} withAuth 是否要校验登录态
 * @returns {object} 增加登录态后的参数
 */
const modifyAuthParam = async (param, withAuth) => {
    const requestParam = { ...param };
    if (withAuth) {
        const { userId, token } = await getAuthInfo();
        requestParam.userId = userId;
        requestParam.token = token;
        return requestParam;
    }
    delete requestParam.userId;
    delete requestParam.userid;
    delete requestParam.token;
    return requestParam;
};
let reqInstance = null;
/**
 * @public
 * @class Request
 * @classdesc 网络请求类,对签名、鉴权等逻辑进行封装处理,用于向腾讯出行服务平台后台发送网络请求
 */
export default class Request {
    /**
     * 默认的request host域名
     * defaultHost 在runtime初始化时进行设置,为出行服务接入层域名
     * 具体业务模块 new Request() 使用时,不指定自定义 host ,将使用defaultHost
     */
    static defaultHost = '';
    /**
     * 默认的request 签名密钥
     * defaultSecretKey 在runtime初始化时进行设置,为当前小程序密钥
     * 具体业务模块 new Request() 使用时,不指定自定义 secretKey ,将使用 defaultSecretKey
     */
    static defaultSecretKey = '';
    static getInstance() {
        if (reqInstance === null) {
            reqInstance = new Request();
        }
        return reqInstance;
    }
    host = '';
    secretKey = '';
    withAuth = true;
    baseParam = {};
    /**
     * Request 构造函数
     * @param {Object} config 构造参数
     * @param {Object} config.withAuth 是否填充登录态参数
     * @param {Object} config.host 自定义的host域名
     * @param {Object} config.baseParam 默认携带的参数
     */
    constructor(config = { withAuth: true }) {
        if (config.host) {
            this.host = config.host;
        }
        this.secretKey = config.secretKey || Request.defaultSecretKey;
        if (typeof config.withAuth !== 'undefined') {
            this.withAuth = !!config.withAuth;
        }
        this.baseParam = config.baseParam || {};
    }
    /**
     * 格式化接口路径
     * @private
     * @param {string} path 需要格式化的接口路径
     * @returns {string} 格式化后的接口路径
     */
    makeUrl(path) {
        if ((/^http/i).test(path))
            return path;
        const host = this.host || Request.defaultHost;
        const validHost = (/^http/i).test(host) ? host : `https://${host}`;
        return `${validHost}/${path}`;
    }
    ;
    /**
     * @public
     * @memberof Request
     * @param {String} path 请求接口路径
     * @param {Object} [param] 请求参数
     * @param {Object} [header] 自定义请求头
     * @returns {Promise} 接口响应
     * @example
     * const $ = getApp().tms.createRequest();
     * $.get(apiPath)
     *  .then((data) => {
     *    // data {Object} 响应数据
     *    // {
     *    //   errCode {Number} 接口响应状态码
     *    //   errMsg  {String} 接口响应状态信息
     *    //   resData {Object} 接口返回数据
     *    // }
     *  })
     *  .catch((e) => {
     *    // e {Object} 错误信息
     *  });
     */
    get(path, param, header) {
        return this.doRequest(path, param, 'GET', header);
    }
    /**
     * @public
     * @memberof Request
     * @param {String} path 请求接口路径
     * @param {Object} [param] 请求参数
     * @param {Object} [header] 自定义请求头
     * @returns {Promise} 接口响应
     * @example
     * const $ = getApp().tms.createRequest();
     * $.post(apiPath)
     *  .then((data) => {
     *    // data {Object} 响应数据
     *    // {
     *    //   errCode {Number} 接口响应状态码
     *    //   errMsg  {String} 接口响应状态信息
     *    //   resData {Object} 接口返回数据
     *    // }
     *  })
     *  .catch((e) => {
     *    // e {Object} 错误信息
     *  });
     */
    post(path, param, header) {
        return this.doRequest(path, param, 'POST', header);
    }
    /**
     * 发送get方式的请求,该方法会返回wx.request全量的返回值(含data,header,cookies,statusCode)
     * @memberof Request
     * @param {string} path 请求接口路径
     * @param {object} param 业务参数
     * @param {object} header 自定义请求头
     * @returns {promise} 接口请求promise
     */
    execGet(path, param, header) {
        return this.createRequestTask(path, param, 'GET', header);
    }
    /**
     * 发送post方式的请求,该方法会返回wx.request全量的返回值(含data,header,cookies,statusCode等)
     * @memberof Request
     * @param {string} path 请求接口路径
     * @param {object} param 业务参数
     * @param {object} header 自定义请求头
     * @returns {promise} 接口请求promise
     */
    execPost(path, param, header) {
        return this.createRequestTask(path, param, 'POST', header);
    }
    /**
     * @memberof Request
     * @param {String} path 请求接口路径
     * @param {String} filePath 上传文件的本地路径
     * @param {Object} param 需要携带的其他参数
     * @param {Object} header 自定义的请求头
     * @returns {Object} 接口返回结果
     */
    async upload(path, filePath, param, header) {
        const requestParam = await composeParam(param, this.withAuth, this.baseParam);
        const res = await new Promise((resolve, reject) => {
            wx.uploadFile({
                name: 'content',
                url: this.makeUrl(path),
                filePath,
                formData: sign(requestParam, this.secretKey),
                header,
                success: resolve,
                fail: reject,
            });
        });
        if (typeof res?.data === 'string') {
            return safeJsonParse(res?.data);
        }
        return res?.data;
    }
    /**
     * @memberof Request
     * @param {string} path 请求接口路径
     * @param {object} param 业务参数
     * @param {string} method 请求方法 get/post
     * @param {object} header 自定义的请求头
     * @returns {object} 接口返回结果
     */
    async doRequest(path, param = {}, method = 'POST', header = {}) {
        const res = await this.createRequestTask(path, param, method, header);
        if (typeof res?.data === 'string') {
            return safeJsonParse(res?.data);
        }
        return res?.data;
    }
    /**
     * 序列化一个 get 请求地址
     * @memberof Request
     * @param {string} path 请求接口路径
     * @param {object} data 业务参数
     * @returns {Promise} 返回序列化之后的 get 请求地址
     */
    async serialize(path, data = {}) {
        let url = this.makeUrl(path);
        const signData = await composeParam(data, this.withAuth, this.baseParam);
        const signature = sign(signData, this.secretKey);
        const params = [];
        Object.keys(signature).forEach((key) => {
            const val = encodeURIComponent(signature[key]);
            params.push(`${key}=${val}`);
        });
        if (params.length)
            url += (/\?/.test(url) ? '&' : '?') + params.join('&');
        return Promise.resolve({ url });
    }
    /**
     * 创建发送请求任务
     * @memberof Request
     * @param {string} path 请求接口路径
     * @param {string} param 业务参数
     * @param {string} method 请求方法 get/post
     * @param {object} header 自定义的请求头
     * @returns {Promise} 接口返回结果
     */
    async createRequestTask(path, param = {}, method = 'POST', header = {}) {
        const requestParam = await composeParam(param, this.withAuth, this.baseParam);
        const data = sign(requestParam, this.secretKey);
        return new Promise((resolve, reject) => {
            const requestTime = Date.now();
            const printLog = (isSuccess, res) => {
                // 埋点已经单独打日志了,接口请求数据日志太长影响分析
                if (path.indexOf('basic/event/upload') !== -1) {
                    return;
                }
                // 打车、代驾日志不截断,方便用于回放
                const fullApi = path.indexOf('/takecar/') !== -1 || path.indexOf('dd/api/v2/order') !== -1;
                let result = JSON.stringify(res);
                if (isSuccess && !fullApi && result.length > 500) {
                    result = `${result.substring(0, 500)} 内容太长被截断`;
                }
                const obj = {
                    path,
                    method,
                    header: JSON.stringify(header),
                    params: JSON.stringify(data),
                    duration: Date.now() - requestTime,
                };
                if (isSuccess) {
                    obj.res = result;
                }
                else {
                    obj.err = result;
                }
                const str = JSON.stringify(obj, null, '  ').replace(/\\"/ig, '\'');
                if (isSuccess) {
                    logger.log(`接口请求成功:\n${str}`);
                }
                else {
                    logger.warn(`接口请求失败:\n${str}`);
                }
            };
            wx.request({
                url: this.makeUrl(path),
                header,
                method,
                data,
                enableHttp2: true,
                success: (res) => {
                    printLog(true, res.data);
                    resolve(res);
                },
                fail: (err) => {
                    printLog(false, err);
                    reject(err);
                },
            });
        });
    }
}
 

# Request

网络请求类,对签名、鉴权等逻辑进行封装处理,用于向腾讯出行服务平台后台发送网络请求

# Promise get

参数

字段名 类型 说明 默认值 是否可选
path String 请求接口路径 ---
param Object 请求参数 ---
header Object 自定义请求头 ---

返回值

类型 说明
Promise 接口响应

示例代码

Example

const $ = getApp().tms.createRequest();
$.get(apiPath)
 .then((data) => {
   // data {Object} 响应数据
   // {
   //   errCode {Number} 接口响应状态码
   //   errMsg  {String} 接口响应状态信息
   //   resData {Object} 接口返回数据
   // }
 })
 .catch((e) => {
   // e {Object} 错误信息
 });

# Promise post

参数

字段名 类型 说明 默认值 是否可选
path String 请求接口路径 ---
param Object 请求参数 ---
header Object 自定义请求头 ---

返回值

类型 说明
Promise 接口响应

示例代码

Example

const $ = getApp().tms.createRequest();
$.post(apiPath)
 .then((data) => {
   // data {Object} 响应数据
   // {
   //   errCode {Number} 接口响应状态码
   //   errMsg  {String} 接口响应状态信息
   //   resData {Object} 接口返回数据
   // }
 })
 .catch((e) => {
   // e {Object} 错误信息
 });

# promise execGet

发送get方式的请求,该方法会返回wx.request全量的返回值(含data,header,cookies,statusCode)

参数

字段名 类型 说明 默认值 是否可选
path string 请求接口路径 ---
param object 业务参数 ---
header object 自定义请求头 ---

返回值

类型 说明
promise 接口请求promise

# promise execPost

发送post方式的请求,该方法会返回wx.request全量的返回值(含data,header,cookies,statusCode等)

参数

字段名 类型 说明 默认值 是否可选
path string 请求接口路径 ---
param object 业务参数 ---
header object 自定义请求头 ---

返回值

类型 说明
promise 接口请求promise

# Object upload

参数

字段名 类型 说明 默认值 是否可选
path String 请求接口路径 ---
filePath String 上传文件的本地路径 ---
param Object 需要携带的其他参数 ---
header Object 自定义的请求头 ---

返回值

类型 说明
Object 接口返回结果

# object doRequest

参数

字段名 类型 说明 默认值 是否可选
path string 请求接口路径 ---
param object 业务参数 ---
method string 请求方法 get/post ---
header object 自定义的请求头 ---

返回值

类型 说明
object 接口返回结果

# Promise serialize

序列化一个 get 请求地址

参数

字段名 类型 说明 默认值 是否可选
path string 请求接口路径 ---
data object 业务参数 ---

返回值

类型 说明
Promise 返回序列化之后的 get 请求地址

# Promise createRequestTask

创建发送请求任务

参数

字段名 类型 说明 默认值 是否可选
path string 请求接口路径 ---
param string 业务参数 ---
method string 请求方法 get/post ---
header object 自定义的请求头 ---

返回值

类型 说明
Promise 接口返回结果

# exports

# rpx

pr\rpx互相转化

点击查看源码
 /**
 * @desc: pr\rpx互相转化
 */
import syncApi from './syncfnmanager';
/**
 * @description rpx to px
 * @param    {Number} rpx 需要转换的rpx数值
 * @returns  {Number}     转换后的px数值
 */
const rpxToPx = (rpx) => {
    const sys = syncApi.getSystemInfoSync();
    const ww = sys.windowWidth;
    const ratio = ww / 750;
    return rpx * ratio;
};
/**
 * @description px to rpx
 * @param {Number} px 需要转换的px数值
 * @returns {Number}  转换后的rpx数值
 */
const pxToRpx = (px) => {
    const sys = syncApi.getSystemInfoSync();
    const ww = sys.windowWidth;
    const ratio = 750 / ww;
    return px * ratio;
};
export { rpxToPx, pxToRpx, };
 

# Number rpxToPx

rpx to px

参数

字段名 类型 说明 默认值 是否可选
rpx Number 需要转换的rpx数值 ---

返回值

类型 说明
Number 转换后的px数值

# Number pxToRpx

px to rpx

参数

字段名 类型 说明 默认值 是否可选
px Number 需要转换的px数值 ---

返回值

类型 说明
Number 转换后的rpx数值

# runtime-car

车辆相关

点击查看源码
 /**
 * @module: runtime-car
 * @desc: 车辆相关
 */
let carInfo = {}; // 当前车信息
let carList = []; // 缓存到的车列表信息
let getCarListProm = null;
/**
 * @class CarService
 * @classdesc 维护整个小程序逻辑中的当前车概念,提供当前车信息与获取车列表接口
 */
export default class CarService {
    /**
     * @memberof CarService
     * getCarInfo 方法 获取当前车信息
     * @returns {object} 车信息对象
     */
    static getCarInfo() {
        if (carInfo?.wecarId) {
            return carInfo;
        }
        carInfo = wx.getStorageSync('currentCar') || {};
        return carInfo;
    }
    /**
     * @memberof CarService
     * setCarInfo 方法 设置当前车信息,必传 param.wecarId
     * @param {object} param 要设置的当前车信息
     * @returns {void}
     */
    static setCarInfo(param = {}) {
        let info = { ...param };
        if (info.wecarId) {
            carList.some((item) => {
                if (item.wecarId === info.wecarId) {
                    info = { ...item, ...info };
                    return true;
                }
                return false;
            });
        }
        carInfo = { ...info };
        wx.setStorageSync('currentCar', { ...carInfo });
    }
    /**
     * @memberof CarService
     * getCarList 方法 获取用户车列表数据
     * @returns {promise} 获取车列表的promise
     */
    static getCarList() {
        // 防止并发请求车列表
        if (getCarListProm) {
            return getCarListProm;
        }
        getCarListProm = new Promise(async (resolve, reject) => {
            const app = getApp({ allowDefault: true });
            app.tms.createRequest().post('user/carlist', {})
                .then((res) => {
                getCarListProm = null;
                if (res.errCode === 0) {
                    carList = res.resData.list.map(car => ({
                        ...car,
                        register: car.vin !== '' && car.plate !== '' && car.engineNo !== '' && car.v2ModelName !== '' && car.v2BrandName !== '',
                    }));
                    carList.some((item) => {
                        if (item.wecarId === carInfo.wecarId) {
                            carInfo = { ...item, ...carInfo };
                            return true;
                        }
                        return false;
                    });
                    resolve(carList);
                }
                else {
                    reject(res);
                }
            })
                .catch((res) => {
                getCarListProm = null;
                reject(res);
            });
        });
        return getCarListProm;
    }
}
 

# CarService

维护整个小程序逻辑中的当前车概念,提供当前车信息与获取车列表接口

# carInfo

# runtime-index

集成业务通用接口(登录、车辆、微信支付分)

点击查看源码
 /* eslint no-underscore-dangle: 0 */
/**
 * @copyright 2021-present, Tencent, Inc. All rights reserved.
 * @module: runtime-index
 * @desc: 集成业务通用接口(登录、车辆、微信支付分)
 */
import Login from './login';
import Car from './car';
import getOpenAppTrafficData from './getopenapptrafficdata';
const { loginFn, getOpenId, getMycarPubOpenId, getSinanPubOpenId, getPhone, registerPhone } = Login;
/**
 * __resolver__ 用于维护 app.tms.fn 中数据的填充
 * app.tms.fn 中的部分数据依赖初始化流程完成,属于异步获取,
 * __resolver__ 属于内部变量,不应在运行时初始化流程外部使用,不做导出。
 * @private
 */
const __resolver__ = {};
/**
 * tms.getLoginInfo 用于提供给业务模块获取当前运行时的登录信息
 * @private
 */
const loginInfoPromise = new Promise(resolver => __resolver__.getLoginInfo = resolver);
/**
 * @public
 * @description 获取用户userId等登录信息
 * @returns {Promise<UserInfo>} 返回用户登录信息
 * @returns {UserInfo.userId} 用户userId
 * @returns {UserInfo.token}  用户token
 * @returns {UserInfo.openId} 用户openId
 * @returns {UserInfo.firstLogin}  用户登录类型:0-非首次登录,1-首次登录
 * @example
 * getApp().tms.getLoginInfo().then((res) => {
 *   // res {Object} 返回数据
 *   // {
 *   //   userId {String} 用户userId
 *   //   token {String} 用户token
 *   //   openId {String} 用户openId
 *   //   firstLogin {Boolean} 是否首次登录
 *   // }
 * });
 */
const getLoginInfo = () => loginInfoPromise;
/**
 * @public
 * @description 获取用户userId等登录信息
 * @param autoLogin 未登录时是否自动登录(小程序中启动时就已登录,该参数用于uniapp的h5页面)
 */
function checkLogin(autoLogin = false) {
    // uniapp的h5会覆盖checkLogin函数实现检查和自动登录功能
    return getLoginInfo().then(() => true);
}
/**
 * tms.getCarManager 用户获取小程序统一维护的车信息管理器
 * @private
 * @returns {object} 车信息管理器
 */
const getCarManager = () => Car;
/**
 * login方法用于完成小程序登录
 * @private
 * @returns {object} 登录成功后的用户信息
 * 返回数据示意
 * {
 *   userId: "1135608",
 *   token: "xxxxxx",
 *   firstLogin: 0,
 * },
 */
const login = async () => {
    const res = await loginFn();
    const loginInfo = { ...res, userId: res.uid };
    __resolver__.getLoginInfo(loginInfo);
    return loginInfo;
};
/**
 * 设置微信支付分授权
 * @param {Object} params对象类型 以下是params的解释
 * @param {Number}params.traceSource 请求来源: 1-订单 2-支付网关 3-车机网关 4-开放平台 6-分账
 * @param {String } param.subMchId 商户id
 * @param {Number} params.bussClassify 请求来源: 1-洗车 2-停车 12-代驾 24-租车 13-保养 3-加油
 * @param {Number} params.payChannel  请求来源: 1-小程序 2-车机 3-小场景 4-开放平台 5-H5
 * @param {String} params.wechaId 公众号id,支付分授权相关一般都用我的车的车公众号
 * @param {String} params.subAppId 小程序的id,支付分授权相关一般都用我的车小程序id
 * @param {String} params.openId 用户在我的车公众号下面的id
 * @returns { object } { state: 1, errMsg: 200, errMsg: '', errData: '' }
 */
async function setPaypointAuth(param = {}) {
    const request = getRequestInstnce();
    const userInfo = await getLoginInfo();
    const req = {
        traceSource: param.traceSource || 1,
        subMchId: param.subMchId || '1602483333',
        bussClassify: param.bussClassify || 24,
        payChannel: param.payChannel || 1,
        subAppId: param.appId || 'wx735359beafd78b9f',
        appId: param.wechaId || 'wx29d15d946a7f2351',
        ...userInfo,
        openId: userInfo.mycarPubOpenId,
    };
    const res = await request.post('paypoint/setpayauth', req);
    if (res.errCode === 0) {
        try {
            await wx.navigateToMiniProgram({
                appId: 'wxd8f3793ea3b935b8',
                path: 'pages/use/enable',
                extraData: {
                    apply_permissions_token: res.resData.token,
                },
            });
        }
        catch (err) {
            return { status: 0, err };
        }
        return { status: 1, ...res };
    }
    return { status: 0, ...res };
}
// 获取项目中初始化的request实例
function getRequestInstnce() {
    const { tms } = getApp();
    const request = tms.createRequest();
    return request;
}
/**
 * 获取微信支付分授权
 * @param {Object} params对象类型 以下是params的解释
 * @param {Number} params.traceSource 请求来源: 1-订单 2-支付网关 3-车机网关 4-开放平台 6-分账
 * @param {Number} param.payChannel 请求来源: 1-小程序 2-车机 3-小场景 4-开放平台 5-H5
 * @returns { object } { errMsg: 200, errMsg: '', errData: '' }
 */
async function getPermissionList(param = {}) {
    const request = getRequestInstnce();
    this.userInfo = await getLoginInfo();
    const data = {
        traceSource: param.traceSource || 1,
        payChannel: param.payChannel || 1, // 请求来源
    };
    const res = await request.post('paypoint/getpermissionlist', data);
    return res;
}
/**
 * 获取微信支付分授权
 * @param {Object} params对象类型 以下是params的解释
 * @param {Number} params.traceSource 请求来源: 1-订单 2-支付网关 3-车机网关 4-开放平台 6-分账
 * @param {String} params.subMchId 商户id
 * @param {Number} params.bussClassify请求来源: 1-洗车 2-停车 12-代驾 24-租车 13-保养 3-加油
 * @param {Number} params.payChannel请求来源: 1-小程序 2-车机 3-小场景 4-开放平台 5-H5
 * @param {String} params.wechaId 公众号id,支付分授权相关一般都用我的车的车公众号
 * @param {String} params.subAppId小程序的id,支付分授权相关一般都用我的车小程序id
 * @param {String} params.openId用户在我的车公众号下面的id
 * @returns { object } { state: 1, errMsg: 200, errMsg: '', errData: '' }
 */
async function terminatePaypointPermisson(param = {}) {
    const request = getRequestInstnce();
    const userInfo = await getLoginInfo();
    const data = {
        traceSource: param.traceSource || 1,
        subMchId: param.subMchId || '1602483333',
        bussClassify: param.bussClassify || 24,
        payChannel: param.payChannel || 1,
        subAppId: param.appId || 'wx735359beafd78b9f',
        appId: param.wechaId || 'wx29d15d946a7f2351',
        reason: param.reason || '其他',
        serviceId: param.serviceId,
        ...userInfo,
        openId: userInfo.mycarPubOpenId,
    };
    const res = await request.post('paypoint/terminatePaypointPermisson', data);
    return res;
}
/**
 * 获取用户在某个业务中是否授权
 * @param {Object} params对象类型 以下是params的解释
 * @param {Number} params.traceSource 请求来源: 1-订单 2-支付网关 3-车机网关 4-开放平台 6-分账
 * @param {Number} params.bussClassify 请求来源: 1-洗车 2-停车 12-代驾 24-租车 13-保养 3-加油
 * @param {Number} params.payChannel 请求来源: 1-小程序 2-车机 3-小场景 4-开放平台 5-H5
 * @returns { object } { state: 1, errMsg: 200, errMsg: '', errData: '' }
 */
async function queryServicePermissions(param = {}) {
    const request = getRequestInstnce();
    this.userInfo = await getLoginInfo();
    const data = {
        traceSource: param.traceSource || 1,
        payChannel: param.payChannel || 1,
        bussClassify: param.payChannel || 24, // 业务类型
    };
    const res = await request.post('paypoint/queryServicePermissions', data);
    return res;
}
/**
 * @namespace runtimeapi
 * @description 对外暴露的api
 * @param {Function} getPhone 获取手机号
 * @param {Function} login... 详见跳转[runtime-login](#runtime-login)
 * @param {Function} getLoginInfo 返回 loginInfoPromise
 * @param {Function} getOpenId 获取openId  详见跳转[runtime-login](#runtime-login)
 * @param {Function} getMycarPubOpenId 获取用户在我的车公众号下的openId 详见跳转[runtime-login](#runtime-login)
 * @param {Function} getSinanPubOpenId 获取用户在出行服务公众号下的openId 详见跳转[runtime-login](#runtime-login)
 * @param {Function} getCarManager 用户获取小程序统一维护的车信息管理器,详见跳转 [runtimt-car](#runtime-car)
 * @param {Function} setPaypointAuth 设置微信支付分授权
 * @param {Function} getPermissionList 获取微信支付分授权
 * @param {Function} terminatePaypointPermisson 获取微信支付分授权
 * @param {Function} queryServicePermissions 获取用户在某个业务中是否授权
 */
const api = {
    getPhone,
    registerPhone,
    login,
    checkLogin,
    getLoginInfo,
    getOpenId,
    getMycarPubOpenId,
    getSinanPubOpenId,
    getCarManager,
    setPaypointAuth,
    getPermissionList,
    terminatePaypointPermisson,
    getOpenAppTrafficData,
    queryServicePermissions,
    /**
     * 对外暴露__resolver__变量,在主项目未完成迁移时,登录完成后,将登录态同步给runtime
     * 注意这并不是架构升级的最终态,在完成主项目的迁移后,我们将不在对外暴露__resolver__
     * @private
     */
    __resolver__,
};
export default api;
 

# namespace runtimeapi

对外暴露的api

参数

字段名 类型 说明 默认值 是否可选
getPhone function 获取手机号 ---
login... function 详见跳转runtime-login ---
getLoginInfo function 返回 loginInfoPromise ---
getOpenId function 获取openId 详见跳转runtime-login ---
getMycarPubOpenId function 获取用户在我的车公众号下的openId 详见跳转runtime-login ---
getSinanPubOpenId function 获取用户在出行服务公众号下的openId 详见跳转runtime-login ---
getCarManager function 用户获取小程序统一维护的车信息管理器,详见跳转 runtimt-car ---
setPaypointAuth function 设置微信支付分授权 ---
getPermissionList function 获取微信支付分授权 ---
terminatePaypointPermisson function 获取微信支付分授权 ---
queryServicePermissions function 获取用户在某个业务中是否授权 ---

# Promise.<UserInfo> getLoginInfo

获取用户userId等登录信息

返回值

类型 说明
Promise.<UserInfo> 返回用户登录信息
UserInfo.userId 用户userId
UserInfo.token 用户token
UserInfo.openId 用户openId
UserInfo.firstLogin 用户登录类型:0-非首次登录,1-首次登录

示例代码

Example

getApp().tms.getLoginInfo().then((res) => {
  // res {Object} 返回数据
  // {
  //   userId {String} 用户userId
  //   token {String} 用户token
  //   openId {String} 用户openId
  //   firstLogin {Boolean} 是否首次登录
  // }
});

# checkLogin

获取用户userId等登录信息

参数

字段名 类型 说明 默认值 是否可选
autoLogin 未登录时是否自动登录(小程序中启动时就已登录,该参数用于uniapp的h5页面) ---

# object setPaypointAuth

设置微信支付分授权

参数

字段名 类型 说明 默认值 是否可选
params对象类型 Object 以下是params的解释 ---
params.traceSource Number 请求来源: 1-订单 2-支付网关 3-车机网关 4-开放平台 6-分账 ---
param.subMchId String 商户id ---
params.bussClassify Number 请求来源: 1-洗车 2-停车 12-代驾 24-租车 13-保养 3-加油 ---
params.payChannel Number 请求来源: 1-小程序 2-车机 3-小场景 4-开放平台 5-H5 ---
params.wechaId String 公众号id,支付分授权相关一般都用我的车的车公众号 ---
params.subAppId String 小程序的id,支付分授权相关一般都用我的车小程序id ---
params.openId String 用户在我的车公众号下面的id ---

返回值

类型 说明
object { state: 1, errMsg: 200, errMsg: '', errData: '' }

# object getPermissionList

获取微信支付分授权

参数

字段名 类型 说明 默认值 是否可选
params对象类型 Object 以下是params的解释 ---
params.traceSource Number 请求来源: 1-订单 2-支付网关 3-车机网关 4-开放平台 6-分账 ---
param.payChannel Number 请求来源: 1-小程序 2-车机 3-小场景 4-开放平台 5-H5 ---

返回值

类型 说明
object { errMsg: 200, errMsg: '', errData: '' }

# object terminatePaypointPermisson

获取微信支付分授权

参数

字段名 类型 说明 默认值 是否可选
params对象类型 Object 以下是params的解释 ---
params.traceSource Number 请求来源: 1-订单 2-支付网关 3-车机网关 4-开放平台 6-分账 ---
params.subMchId String 商户id ---
params.bussClassify请求来源: Number 1-洗车 2-停车 12-代驾 24-租车 13-保养 3-加油 ---
params.payChannel请求来源: Number 1-小程序 2-车机 3-小场景 4-开放平台 5-H5 ---
params.wechaId String 公众号id,支付分授权相关一般都用我的车的车公众号 ---
params.subAppId小程序的id,支付分授权相关一般都用我的车小程序id String ---
params.openId用户在我的车公众号下面的id String ---

返回值

类型 说明
object { state: 1, errMsg: 200, errMsg: '', errData: '' }

# object queryServicePermissions

获取用户在某个业务中是否授权

参数

字段名 类型 说明 默认值 是否可选
params对象类型 Object 以下是params的解释 ---
params.traceSource Number 请求来源: 1-订单 2-支付网关 3-车机网关 4-开放平台 6-分账 ---
params.bussClassify Number 请求来源: 1-洗车 2-停车 12-代驾 24-租车 13-保养 3-加油 ---
params.payChannel Number 请求来源: 1-小程序 2-车机 3-小场景 4-开放平台 5-H5 ---

返回值

类型 说明
object { state: 1, errMsg: 200, errMsg: '', errData: '' }

# runtime-login

登录相关

点击查看源码
 /* eslint-disable valid-jsdoc */
/**
 * @copyright 2021-present, Tencent, Inc. All rights reserved.
 * @module: runtime-login
 * @desc: 登录相关
 * @brief login.js 用于维护 runtime 框架的用户登录流程,获取用户的登录的可信凭证.
 * runtime初始化时,会调用登录流程,基于runtime的后续业务代码,不必关注用户的登录状态,
 * 当前,登录流程仅支持【腾讯出行服务小程序】,我们计划支持出行服务公众号的H5开发,基于runtime的
 * 微信H5开发,不必关注微信的授权登录流程(即重定向微信页面,换取token等),H5的登录流程也收敛到
 * login.js中。
 * 另外,runtime 对【我的车小程序】的支持,以及后续对其他基于出行服务架构的小程序支持,都在我们规划之内。
 *
 *
 * getOpenAppTrafficData, 此方法用于获取登录用户来源,用于给用户打标签等,打标签耦合在登录接口中,
 * 因此这部分代码迁移到了 tms-rumtine 中,
 * 从 runtime 的角度考虑,登录流程应尽量纯粹,打标签应属于业务流程范畴,与 runtime 无关。
 * 后续我们拓展 runtime,使之适用于不同小程序和H5项目时,其登录流程不一定有打标签这一动作。
 *
 */
import getOpenAppTrafficData from './getopenapptrafficdata';
/**
 * 调用wx.login获取登录code
 * @private
 */
const getCode = () => new Promise((resolve, reject) => {
    wx.login({ success: ({ code }) => resolve(code), fail: reject });
});
let loginProcessing = false; // 是否有进行中的登录流程
/**
 * login 方法 获取用户信息
 * @private
 * @return {object} userInfo
 */
const loginFn = async () => {
    if (loginProcessing)
        return;
    loginProcessing = true;
    let userInfo = {};
    try {
        let userData = await login();
        if (userData.errCode === 10 || !userData.resData.userInfo || !userData.resData.userInfo.uid) {
            userData = await login();
        }
        if (userData.errCode === 0) {
            wx.setStorageSync('userInfo', userData.resData.userInfo);
            userInfo = userData.resData.userInfo;
        }
        loginProcessing = false;
    }
    catch (e) {
        loginProcessing = false;
    }
    return userInfo;
};
/**
 * 登录重试接口
 * @private
 * @returns { object } errCode 接口返回错误码
 * @returns { object } resData 用户信息
 */
async function login() {
    let userData = {};
    try {
        const code = await getCode();
        // @ts-ignore
        const { trafficEntrence: registerSource, scene: sceneId } = getOpenAppTrafficData();
        userData = await getApp().tms.createRequest({ withAuth: false }).post('user/login', { code, registerSource, sceneId });
    }
    catch (e) {
        console.error(e); // eslint-disable-line
    }
    return {
        errCode: userData.errCode,
        resData: userData.resData || {},
    };
}
let getOpenIdProm; // 获取用户在小程序中的openId状态
/**
 * @return {string} openId
 * 获取成功时,返回resolved Promise,resolved数据为openId
 * 获取失败时,返回rejected Promise,rejected数据为错误对象
 * @description 获取用户在小程序中的openId
 */
const getOpenId = async () => {
    if (getOpenIdProm) { // 避免重复获取
        return getOpenIdProm;
    }
    // eslint-disable-next-line @typescript-eslint/no-misused-promises
    getOpenIdProm = new Promise(async (resolve, reject) => {
        getApp().tms.callCloudFunc('user', { $url: 'user/getOpenId' })
            .then((res) => {
            const openId = res?.result?.resData?.openId;
            if (openId) {
                resolve(openId);
            }
            getOpenIdProm = null;
            reject(new Error('No openId found in cloud function res'));
        })
            .catch((res) => {
            getOpenIdProm = null;
            reject(res);
        });
    });
    return getOpenIdProm;
};
/**
 * 获取用户在指定公众号下的openId
 * @private
 * @return {Promise<String>}
 * 获取成功时,返回resolved Promise,resolved数据为用户在指定公众号下的openId
 * 获取失败时,返回rejected Promise,rejected数据为错误对象
 */
const getOaPubOpenId = oaName => getApp().tms.createRequest({ withAuth: true }).post('user/getpubopenid', { oaName })
    .then((res) => {
    const { errCode, resData } = res;
    if (errCode !== 0) {
        return Promise.reject(res);
    }
    const { mycarPubOpenId, sinanPubOpenId } = resData;
    if (oaName === 'sinan') {
        return sinanPubOpenId
            || Promise.reject(new Error(`No sinanPubOpenId found in response ${JSON.stringify(res)}`));
    }
    return mycarPubOpenId || Promise.reject(new Error(`No mycarPubOpenId found in response ${JSON.stringify(res)}`));
});
let getSinanPubOpenIdProm;
let getMycarPubOpenIdProm;
/**
 * @description 获取用户在我的车公众号下的openId
 * @return {Promise<String>}
 * 获取成功时,返回resolved Promise,resolved数据为用户在我的车公众号下的openId
 * 获取失败时,返回rejected Promise,rejected数据为错误对象
 */
const getMycarPubOpenId = () => {
    if (getMycarPubOpenIdProm) { // 避免重复获取
        return getMycarPubOpenIdProm;
    }
    getMycarPubOpenIdProm = getOaPubOpenId('mycar')
        .then(openId => openId || Promise.reject(new Error('No mycarPubOpenId found')))
        .catch((e) => {
        getMycarPubOpenIdProm = null;
        return Promise.reject(e);
    });
    return getMycarPubOpenIdProm;
};
/**
 * @description 获取用户在出行服务公众号下的openId
 * @return {Promise<String>}
 * 获取成功时,返回resolved Promise,resolved数据为用户在出行服务公众号下的openId
 * 获取失败时,返回rejected Promise,rejected数据为错误对象
 */
const getSinanPubOpenId = () => {
    if (getSinanPubOpenIdProm) { // 避免重复获取
        return getSinanPubOpenIdProm;
    }
    getSinanPubOpenIdProm = getOaPubOpenId('sinan')
        .then(openId => openId || Promise.reject(new Error('No sinanPubOpenId found')))
        .catch((e) => {
        getSinanPubOpenIdProm = null;
        return Promise.reject(e);
    });
    return getSinanPubOpenIdProm;
};
/**
 * @public
 * @description 获取用户手机号
 * @return {Promise<String|Object>} 手机号
 * @example
 * getApp().tms.getPhone()
 *  .then((phone) => {
 *    // ...
 *  })
 *  .catch((e) => {
 *    // ...
 *  });
 */
const getPhone = () => getApp().tms.createRequest({ withAuth: true }).post('user/phone/fetch')
    .then((res) => {
    if (res && res.errCode === 0) {
        return (res.resData?.phoneno) || '';
    }
    return Promise.reject(res);
});
/**
 * @description 绑定手机号
 * @param {Object} data    params 加密数据对象
 * @param {string} data.encryptedData button组件bindgetphonenumber事件返回的encryptedData字段
 * @param {string} data.iv button组件bindgetphonenumber事件返回的iv字段
 * @returns {Object} data    绑定结果回调
 * @returns {number} data.success 是否绑定成功 boolean类型
 * @returns {number} data.phone  绑定成功时的手机号,失败则为空
 * @returns {number} data.errMsg 绑定结果描述信息
 * @example
 * const { tms } = getApp({ allowDefault: true });
 * const res = await tms.registerPhone({ encryptedData, iv })
 * if(res.success) {
 *    console.log('phone', res.phone)
 * } else {
 *    // 注册失败处理逻辑
 *    console.log(res.errMsg)
 * }
 */
async function registerPhone({ encryptedData, iv }) {
    try {
        const result = await getApp().tms.createRequest().post('user/phone/regist', {
            iv,
            isCipher: 1,
            phoneno: encryptedData,
        });
        return {
            success: result.errCode === 0,
            phone: result.errCode === 0 ? result.resData.phoneno : '',
            errMsg: result.errMsg,
        };
    }
    catch (error) {
        console.error(error);
        return {
            success: false,
            phone: '',
            errMsg: '绑定失败',
        };
    }
}
const runtimeObj = {
    loginFn,
    getOpenId,
    getPhone,
    registerPhone,
    getMycarPubOpenId,
    getSinanPubOpenId,
};
export default runtimeObj;
 

# string getOpenId

获取用户在小程序中的openId

返回值

类型 说明
string openId获取成功时,返回resolved Promise,resolved数据为openId获取失败时,返回rejected Promise,rejected数据为错误对象

# Promise.<String> getMycarPubOpenId

获取用户在我的车公众号下的openId

返回值

类型 说明
Promise.<String> 获取成功时,返回resolved Promise,resolved数据为用户在我的车公众号下的openId获取失败时,返回rejected Promise,rejected数据为错误对象

# Promise.<String> getSinanPubOpenId

获取用户在出行服务公众号下的openId

返回值

类型 说明
Promise.<String> 获取成功时,返回resolved Promise,resolved数据为用户在出行服务公众号下的openId获取失败时,返回rejected Promise,rejected数据为错误对象

# Promise.<(String|Object)> getPhone

获取用户手机号

返回值

类型 说明
Promise.<(String|Object)> 手机号

示例代码

Example

getApp().tms.getPhone()
 .then((phone) => {
   // ...
 })
 .catch((e) => {
   // ...
 });

# Object registerPhone

绑定手机号

参数

字段名 类型 说明 默认值 是否可选
data Object params 加密数据对象 ---
data.encryptedData string button组件bindgetphonenumber事件返回的encryptedData字段 ---
data.iv string button组件bindgetphonenumber事件返回的iv字段 ---

返回值

类型 说明
Object data 绑定结果回调
number data.success 是否绑定成功 boolean类型
number data.phone 绑定成功时的手机号,失败则为空
number data.errMsg 绑定结果描述信息

示例代码

Example

const { tms } = getApp({ allowDefault: true });
const res = await tms.registerPhone({ encryptedData, iv })
if(res.success) {
   console.log('phone', res.phone)
} else {
   // 注册失败处理逻辑
   console.log(res.errMsg)
}

# storage

处理localstorage相关函数

点击查看源码
 /**
 * @desc: 处理localstorage相关函数
 */
/**
 * 保存数据到localstorage
 * @param key
 * @param data
 * @returns {boolean} true为成功,false为报错
 */
function setItem(key, data) {
    try {
        wx.setStorageSync(key, data);
        return true;
    }
    catch {
        return false;
    }
}
/**
 * 从localstorage取数据
 * @param key
 * @param defaultValue wx接口报错时返回默认值
 * @returns {any}
 */
function getItem(key, defaultValue = null) {
    try {
        const val = wx.getStorageSync(key);
        return val === '' ? defaultValue : val;
    }
    catch {
        return defaultValue;
    }
}
/**
 * 从localstorage中删除
 * @param key
 * @returns {boolean}
 */
function removeItem(key) {
    try {
        wx.removeStorageSync(key);
        return true;
    }
    catch {
        return false;
    }
}
const cleanQueue = [];
let cleanTimerId = 0;
function cleanTask() {
    const { keys } = wx.getStorageInfoSync();
    while (cleanQueue.length > 0) {
        const { key, version } = cleanQueue.pop();
        for (let i = version - 1; i > version - 10 && i > 0; i--) {
            const str = `${key}_v${i}`;
            if (keys.includes(str)) {
                removeItem(str);
            }
        }
    }
    cleanTimerId = 0;
}
/**
 * 缓存组件或页面的缓存data,会缓存到localStorage里
 * @param {string} key localStorage的唯一标志
 * @param {string} version 版本号,低于该版本号的缓存会被异步清除
 * @param {object} data 页面缓存的数据
 * @returns {boolean}
 */
function setCacheData(key, version, data) {
    // 异步清理旧数据,不要阻塞当前线程
    if (!cleanQueue.some(q => q.key === key && q.version === version)) {
        cleanQueue.push({ key, version });
        if (cleanTimerId === 0) {
            cleanTimerId = setTimeout(cleanTask, 2000);
        }
    }
    const str = `${key}_v${version}`;
    return setItem(str, data);
}
/**
 * 获取组件或页面的缓存data
 * @param {string} key localStorage的唯一标志
 * @param {string} version 版本号
 * @param {object} defaultData 接口报错时返回默认值
 * @returns {any}
 */
function getCacheData(key, version, defaultData = null) {
    const str = `${key}_v${version}`;
    return getItem(str, defaultData);
}
export { getItem, setItem, setCacheData, getCacheData, };
 

# boolean setItem

保存数据到localstorage

参数

字段名 类型 说明 默认值 是否可选
key ---
data ---

返回值

类型 说明
boolean true为成功,false为报错

# any getItem

从localstorage取数据

参数

字段名 类型 说明 默认值 是否可选
key ---
defaultValue wx接口报错时返回默认值 ---

返回值

类型 说明
any

# boolean removeItem

从localstorage中删除

参数

字段名 类型 说明 默认值 是否可选
key ---

返回值

类型 说明
boolean

# boolean setCacheData

缓存组件或页面的缓存data,会缓存到localStorage里

参数

字段名 类型 说明 默认值 是否可选
key string localStorage的唯一标志 ---
version string 版本号,低于该版本号的缓存会被异步清除 ---
data object 页面缓存的数据 ---

返回值

类型 说明
boolean

# any getCacheData

获取组件或页面的缓存data

参数

字段名 类型 说明 默认值 是否可选
key string localStorage的唯一标志 ---
version string 版本号 ---
defaultData object 接口报错时返回默认值 ---

返回值

类型 说明
any

# stringUtils

字符串处理相关函数

点击查看源码
 /**
 * @desc: 字符串处理相关函数
 */
/**
 * 字符串截断,处理时按可见字符长度进行截断
 * 可见字符的含义是指:字母、数字、汉字、表情等均等价于一个字符
 * @param {String} str - 原始字符串
 * @param {Number} maxLen - 字符串截断后的最大长度
 * @returns {String} 截断后的字符串;如果确实进行了截断,在最后面加'...';
 */
const subStr = (str, maxLen) => {
    // 按照码点(codePoint),把原始字符串的前maxLen+1个字符放到chars数组中
    const chars = [];
    for (const codePoint of str) { // eslint-disable-line
        chars.push(codePoint);
        if (chars.length > maxLen)
            break;
    }
    // 如果可见字符数量小于等于字符串截断后的最大长度,无需截断,返回原始字符串
    if (chars.length <= maxLen) {
        return str;
    }
    // 可见字符数量多于字符串截断后的最大长度,需要截断,并在末尾添加...
    // 注意,此时返回的字符串是 maxLen-2个可见字符 + ...(maxLen-2是因为...占用2个汉字字符位置)
    return `${chars.splice(0, maxLen - 2).join('')}...`;
};
/**
 * 手机号处理,隐藏中间部分位数
 * 隐藏规则:假设隐藏部分位数后,手机号由ABC构成,其中A、C是可见部分,B是隐藏部分
 * 1. 隐藏部分(B)占手机号总位数的1/3,且隐藏部分的位数向上取整
 * 2. 不被隐藏的部分(A、C)要均匀分布在隐藏部分(B)的两侧,即尽量使AC等长
 * 3. 如果不隐藏部分(A、C)长度无法等长,则使C多显示一位
 * @param {String} phone 手机号
 * @returns {String} 隐藏后的手机号
 */
const hidePhoneCenter = (phone) => {
    const len = phone && phone.length;
    if (!len) {
        return '';
    }
    // 各部分位数
    const center = Math.ceil(len / 3);
    const left = Math.floor((len - center) / 2);
    const right = len - center - left;
    // 各部分字符串
    const centerStr = phone.substr(left, center).replace(/./g, '*');
    const leftStr = phone.substr(0, left);
    const rightStr = phone.substr(-right);
    return `${leftStr}${centerStr}${rightStr}`;
};
/**
 * 格式化车牌
 * 例如:京A12345 -> 京A·12345
 * @param {String} plate 车牌号
 * @returns {String} 格式化后的车牌号
 */
const formatPlate = (plate) => {
    if (!plate || typeof plate !== 'string') {
        return '';
    }
    if (plate.length <= 2 || /·/.test(plate)) {
        return plate;
    }
    return `${plate.substring(0, 2)}·${plate.substring(2)}`;
};
/**
 * 检查手机号是否合法
 * @param {String} phone 手机号
 * @returns {Boolean} 手机号是否合法
 */
const isValidPhone = phone => /^1[\d]{10}$/.test(phone);
/**
 * 检查验证码是否合法
 * @param {String} code 验证码
 * @returns {Boolean} 验证码是否合法
 */
const isValidAuthCode = code => /^[\d]{6}$/.test(code);
const validPlateFirstLetters = ['京', '津', '冀', '晋', '蒙', '辽', '吉', '黑', '沪', '苏', '浙', '皖', '闽', '赣', '鲁',
    '豫', '鄂', '湘', '粤', '桂', '琼', '渝', '川', '贵', '云', '藏', '陕', '甘', '青', '宁', '新', '使', '领'];
/**
 * 检查车牌是否合法
 * @param {String} plate 车牌
 * @returns {Boolean} 车牌是否合法
 */
const isValidPlate = (plate) => {
    if (!plate || typeof plate !== 'string') {
        return false;
    }
    // 检查首位是否是合法的汉字
    const [firstLetter] = plate;
    if (validPlateFirstLetters.indexOf(firstLetter) === -1) {
        return false;
    }
    if (!/[使领警学]/.test(plate)) { // 普通车牌
        const number = plate.substring(1);
        return /^[a-zA-Z][0-9a-zA-Z]{5}$/.test(number) // 第一位是字母,后面5位是字母或数字
            || /^[a-zA-Z][a-zA-Z][0-9a-zA-Z]{5}$/.test(number) // 第一位是字母,第二位是字母,后面再跟5位字母或数字(新能源小车)
            || /^[a-zA-Z][0-9a-zA-Z]{5}[a-zA-Z]$/.test(number) // 第一位是字母,最后一位是字母,中间5位字母或数字(新能源大车)
            || /^粤Z[0-9a-zA-Z]{4,5}[港澳]{1}/.test(plate); // 广东港澳两地车牌
    }
    // return /^[^使领警学]{1}[a-zA-Z][0-9a-zA-Z]{3,4}[使领警学]{1}$/.test(plate) || // 使/领/警/学车牌
    // /^[使领]{1}[0-9a-zA-Z]{5}$/.test(plate); // 使馆领事馆特殊车牌
    return false;
};
/**
 * 四舍五入,并返回格式化的字符串
 * 支持保留n位小数,n>=0,如 roundStr(1.325, 2)=1.33
 * 支持格式化字符串时取出末尾的0,如roundStr(1.109, 2, true)=1.1
 * @param {any} x 原数字, 如果n不是合法数字或者无法转换为合法数字,roundStr结果返回''
 * @param {any} n 保留几位小数,默认0
 * @param {boolean} removeTrailingZero 是否移除字符串末尾的无效数字0
 1. 如果n不是合法数字或者无法转换为合法数字,roundStr结果返回''
 2. 如果n小于0,roundStr结果返回''
 3. 如果n的值包含小数部分,roundStr处理时只关注n的整数部分值
 * @returns {string} 返回四舍五入后的字符串,异常情况下返回空字符串''
 */
const roundStr = (x, n = 2, removeTrailingZero = false) => {
    const nNum = Math.floor(Number(n)); // n转换为数字,且只保留整数部分
    let xNum = Number(x); // x转换为数字
    // 异常情况,返回''
    if (isNaN(xNum) || isNaN(nNum) || nNum < 0) {
        return '';
    }
    // 仅保留整数的情况
    if (nNum === 0) {
        return Math.round(xNum).toString();
    }
    const rexExp = new RegExp(`\\.\\d{${nNum}}5`);
    // 保留n位小数的情况
    // 1. 大部分情况下,四舍五入使用Number.toFixed即可
    // 2. 然而,Number.toFixed方法在某些情况下对第n+1位是5的四舍五入存在问题,如1.325保留2小数时结果为1.32(期望为1.33)
    //    对此种情况下,有两种处理方式:
    //    2.1 先扩大10^n倍,舍掉小数部分取整数部分,然后加1,最后缩小10^n倍
    //        但此种情况下,不能处理过大的数字,也不能处理保留小数位数过多的情况,会可能导致数字超过Infinity
    //    2.2 Number.toFixed是四舍6入,对于第n+1位是5的情况,增加2*10^(-n-1),保证满足第n+1位>6
    //        增加2*10^(-n-1)而不是增加1*10^(-n-1)是因为后者不能保证第n+1位>=6,例如1.325+0.001=1.32599999...第n+1位仍然为5
    // 此处,采用2.2方式,解决Number.toFixed的问题,又能避免2.1方式中数字超过Infinity的问题
    // 情况2,处理方式2.1:如果小数部分第n+1位是5,增加2*10^(-n-1)
    if (rexExp.test(xNum.toString())) {
        xNum += 2 * (10 ** (-nNum - 1));
    }
    const str = xNum.toFixed(nNum);
    if (!removeTrailingZero) {
        return str;
    }
    // 去除末尾的0
    if (/^\d+\.0*$/.test(str)) { // 小数部分全是0
        return str.replace(/^(\d+)(\.0*)$/, (_m, s1) => s1);
    }
    return str.replace(/^(\d+\.\d*[1-9])(0*)$/, (_m, s1) => s1);
};
export { formatPlate, subStr, hidePhoneCenter, isValidPhone, isValidPlate, isValidAuthCode, roundStr, };
 

# String subStr

字符串截断,处理时按可见字符长度进行截断 可见字符的含义是指:字母、数字、汉字、表情等均等价于一个字符

参数

字段名 类型 说明 默认值 是否可选
str String 原始字符串 ---
maxLen Number 字符串截断后的最大长度 ---

返回值

类型 说明
String 截断后的字符串;如果确实进行了截断,在最后面加'...';

# String hidePhoneCenter

手机号处理,隐藏中间部分位数 隐藏规则:假设隐藏部分位数后,手机号由ABC构成,其中A、C是可见部分,B是隐藏部分

  1. 隐藏部分(B)占手机号总位数的1/3,且隐藏部分的位数向上取整
  2. 不被隐藏的部分(A、C)要均匀分布在隐藏部分(B)的两侧,即尽量使AC等长
  3. 如果不隐藏部分(A、C)长度无法等长,则使C多显示一位

参数

字段名 类型 说明 默认值 是否可选
phone String 手机号 ---

返回值

类型 说明
String 隐藏后的手机号

# String formatPlate

格式化车牌 例如:京A12345 -> 京A·12345

参数

字段名 类型 说明 默认值 是否可选
plate String 车牌号 ---

返回值

类型 说明
String 格式化后的车牌号

# Boolean isValidPhone

检查手机号是否合法

参数

字段名 类型 说明 默认值 是否可选
phone String 手机号 ---

返回值

类型 说明
Boolean 手机号是否合法

# Boolean isValidAuthCode

检查验证码是否合法

参数

字段名 类型 说明 默认值 是否可选
code String 验证码 ---

返回值

类型 说明
Boolean 验证码是否合法

# Boolean isValidPlate

检查车牌是否合法

参数

字段名 类型 说明 默认值 是否可选
plate String 车牌 ---

返回值

类型 说明
Boolean 车牌是否合法

# string roundStr

四舍五入,并返回格式化的字符串 支持保留n位小数,n>=0,如 roundStr(1.325, 2)=1.33 支持格式化字符串时取出末尾的0,如roundStr(1.109, 2, true)=1.1

参数

字段名 类型 说明 默认值 是否可选
x any 原数字, 如果n不是合法数字或者无法转换为合法数字,roundStr结果返回'' ---
n any 保留几位小数,默认0 ---
removeTrailingZero boolean 是否移除字符串末尾的无效数字0
  1. 如果n不是合法数字或者无法转换为合法数字,roundStr结果返回''
  2. 如果n小于0,roundStr结果返回''
  3. 如果n的值包含小数部分,roundStr处理时只关注n的整数部分值 | --- | 否 |

返回值

类型 说明
string 返回四舍五入后的字符串,异常情况下返回空字符串''

# syncfnmanager

本文件负责对小程序调用wx同步方法的管理

点击查看源码
 /**
 * @desc: 本文件负责对小程序调用wx同步方法的管理
 */
let systemInfo = null; // 系统信息。
let launchOptions = null; // 启动参数
let accountInfo = null; // 小程序账号信息
/**
 * 获取系统信息。同wx.getSystemInfoSync
 * @returns {Object} 系统信息
 */
const getSystemInfoSync = () => {
    if (systemInfo) {
        return systemInfo;
    }
    try {
        systemInfo = wx.getSystemInfoSync();
        return systemInfo;
    }
    catch (_) {
        return {};
    }
};
/**
 * 重置系统信息,仅用于单元测试
 * @returns {void}
 */
const resetSystemInfoSync = () => {
    systemInfo = null;
};
/**
 * 获取启动参数。同wx.getLaunchOptionsSync
 * @returns {Object} 启动参数
 */
const getLaunchOptionsSync = () => {
    if (launchOptions) {
        return launchOptions;
    }
    try {
        launchOptions = wx.getLaunchOptionsSync();
        return launchOptions;
    }
    catch (_) {
        return {};
    }
};
/**
 * 获取客户端平台。同wx.getSystemInfoSync().platform
 * @returns {String} 平台名称
 */
const getPlatform = () => {
    const UNKNOWN = 'unknown';
    try {
        const systemInfo = getSystemInfoSync();
        const { platform } = systemInfo;
        return platform || UNKNOWN;
    }
    catch (_) {
        return UNKNOWN;
    }
};
/**
 * 获取字符串类型的启动参数, 同wx.getLaunchOptionsSync进行JSON.stringify
 * @returns {String} 序列化的参数字符串
 */
const getLaunchParamOfString = () => {
    try {
        const options = getLaunchOptionsSync() || {};
        return JSON.stringify(options);
    }
    catch (_) {
        return '';
    }
};
/**
 * @description 获取小程序账号信息
 * @returns {Object} 小程序账号信息,返回内容同wx.getAccountInfoSync()
 */
export const getAccountInfoSync = () => {
    if (accountInfo) {
        return accountInfo;
    }
    try {
        accountInfo = wx.getAccountInfoSync();
        return accountInfo;
    }
    catch (_) {
        return {};
    }
};
const obj = {
    getPlatform,
    getSystemInfoSync,
    getLaunchOptionsSync,
    getLaunchParamOfString,
    resetSystemInfoSync,
    getAccountInfoSync,
};
export default obj;
 

# systemInfo

# Object getAccountInfoSync

获取小程序账号信息

返回值

类型 说明
Object 小程序账号信息,返回内容同wx.getAccountInfoSync()

# Object getSystemInfoSync

获取系统信息。同wx.getSystemInfoSync

返回值

类型 说明
Object 系统信息

# void resetSystemInfoSync

重置系统信息,仅用于单元测试

返回值

类型 说明
void

# Object getLaunchOptionsSync

获取启动参数。同wx.getLaunchOptionsSync

返回值

类型 说明
Object 启动参数

# String getPlatform

获取客户端平台。同wx.getSystemInfoSync().platform

返回值

类型 说明
String 平台名称

# String getLaunchParamOfString

获取字符串类型的启动参数, 同wx.getLaunchOptionsSync进行JSON.stringify

返回值

类型 说明
String 序列化的参数字符串

# timeUtils

时间处理相关函数

点击查看源码
 /**
 * @desc: 时间处理相关函数
 * @author:2017-07-26 @davislu modify.
 */
import { round } from './numUtils';
import { roundStr } from './stringUtils';
/**
 * 将时间段进行聚合计算,得出不同时间单位的计数值,例如:3601秒 -> { hour: 1, minute: 0, second: 1 },即:1小时0分1秒
 * @param {Number} seconds 时间段的总秒数
 * @param {String} maxUnit 最大计数单位:second-秒,minute-分,hour-小时,day-天,month-月,year-年;默认 year
 * @param {String} minUnit 最小计数单位:second-秒,minute-分,hour-小时,day-天,month-月,year-年;默认 second
 * @param {Number} decimal 有不足最小单位数值的情况下,保留几位小数;默认 2
 * @returns {Object} result 以下是各计数单位下的数值:
 * @returns {Number} result.year    - 年;  当maxUnit < 'year' 时,此字段永远为0;此字段为-1时,表示 seconds 参数非法 或 minUnit > maxUnit等异常情况
 * @returns {Number} result.month   - 月;  当maxUnit < 'month' 时,此字段永远为0;
 * @returns {Number} result.day     - 天;  当maxUnit < 'day' 时,此字段永远为0;
 * @returns {Number} result.hour    - 小时;当maxUnit < 'hour' 时,此字段永远为0;
 * @returns {Number} result.minute  - 分钟;当maxUnit < 'minute' 时,此字段永远为0;
 * @returns {Number} result.second  - 秒;
 * @returns {Number} result.decimal - 不足最小单位的部分,具体含义与 minUnit 参数相关;例如 minUnit = day 时,此字段表示不足1天的部分
 */
const groupTimeDuration = (seconds, maxUnit = 'year', minUnit = 'second', decimal = 2) => {
    // 时间参数(seconds)检查
    const sec = typeof seconds === 'number' ? seconds : parseFloat(seconds);
    if (isNaN(sec))
        return { year: -1, month: 0, day: 0, hour: 0, minute: 0, second: 0, decimal: 0 };
    // 各时间单位对应的秒数
    const unitSeconds = { year: 31536000, month: 2592000, day: 86400, hour: 3600, minute: 60, second: 1 };
    // 时间单位参数(minUnit,maxUnit)检查
    const minSeconds = unitSeconds[minUnit] || unitSeconds.second;
    const maxSeconds = unitSeconds[maxUnit] || unitSeconds.year;
    if (maxSeconds < minSeconds)
        return { year: -1, month: 0, day: 0, hour: 0, minute: 0, second: 0, decimal: 0 };
    // 截取在 minUnit maxUnit 之间的计数单位,并按从大到小排序
    const allUnitKeys = Object.keys(unitSeconds);
    const groupUnits = allUnitKeys
        .filter(unit => unitSeconds[unit] >= minSeconds && unitSeconds[unit] <= maxSeconds) // 过滤超出范围的单位
        .sort((a, b) => unitSeconds[b] - unitSeconds[a]) // 排序
        .map(unit => ({ name: unit, seconds: unitSeconds[unit] })); // 转换为 { name, seconds } 的形式,方便后面用
    // 各返回字段初始赋值
    const result = { decimal: 0 };
    allUnitKeys.forEach(unit => result[unit] = 0);
    // 从大到小逐个计算赋值
    // const periodArr = [];
    let restSeconds = sec; // 剩余待统计描述
    groupUnits.forEach((unit) => {
        const curUnitNum = Math.floor(restSeconds / unit.seconds); // 高于当前计数单位的数值
        result[unit.name] = curUnitNum; // 例如 { year: 1 }
        restSeconds %= unit.seconds;
        if (unit.seconds === minSeconds)
            result.decimal = round(restSeconds / minSeconds, decimal);
    });
    return result;
};
/**
 * 将时间段进行聚合计算并格式化,得出方便人阅读的文案,例如:3601秒 -> 1小时0分1秒
 * @param {Number} seconds 时间段的总秒数
 * @param {String} maxUnit 最大计数单位:second-秒,minute-分,hour-小时,day-天,month-月,year-年;默认 year
 * @param {String} minUnit 最小计数单位:second-秒,minute-分,hour-小时,day-天,month-月,year-年;默认 second
 * @param {Number} decimal 有不足最小单位数值的情况下,保留几位小数;默认 2
 * @returns {String}
 */
const formatTimeDuration = (seconds, maxUnit = 'year', minUnit = 'second', decimal = 2) => {
    const result = groupTimeDuration(seconds, maxUnit, minUnit, decimal);
    if (result.year === -1)
        return ''; // seconds 不合法 或 maxUnit < minUnit
    const unitWording = { year: '年', month: '月', day: '天', hour: '小时', minute: '分钟', second: '秒' };
    const periodArr = [];
    ['year', 'month', 'day', 'hour', 'minute', 'second'].forEach((unit) => {
        if (unit !== minUnit) {
            if (result[unit] > 0)
                periodArr.push(`${result[unit]}${unitWording[unit]}`); // 如2小时
        }
        else { // 当前unit是最小单位,处理不足最小单位的部分
            const min = round(result[unit] + result.decimal, decimal);
            if (min > 0)
                periodArr.push(`${roundStr(min, 2, true)}${unitWording[unit]}`); // 如1.2,0.2
            else if (periodArr.length === 0)
                periodArr.push(`1${unitWording[unit]}`); // 1 unit内
        }
    });
    return periodArr.join('');
};
/**
 * @function
 * @description 格式化时间,>24小时的显示 【x天】;<1小时的显示  【x分钟】;<24小时&>1小时的显示  【x小时y分钟】
 * @param {Number} seconds 秒数
 * @returns {String} 格式化的时间
 */
const formatTime = (seconds) => {
    if (typeof seconds !== 'number')
        return seconds;
    const { day, hour, minute, decimal } = groupTimeDuration(seconds, 'day', 'minute', 2);
    // >24小时的显示  【x天】
    if (day > 0)
        return `${day}`;
    // <1小时的显示  【x分钟】 ,x取整数上限,最低为1分钟。
    if (hour === 0)
        return `${Math.ceil(minute + decimal)}分钟`;
    // <24小时&>1小时的显示  【x小时y分钟】 ,分钟取整数上限
    let cost = `${hour}小时`;
    const min = Math.ceil(minute + decimal);
    if (min > 0)
        cost += `${min}分钟`;
    return cost;
};
/**
 * @function
 * @description 将秒数格式化: 秒数多于1天显示【x天y小时z分钟】;秒数小于1小时【x分钟】;秒数介于1天和1小时之间显示【x小时y分钟】
 * @param       {Number} seconds 秒数
 * @returns      {String} 格式化后的文案
 */
const formatTimeWithDetails = (seconds) => {
    // 非Number类型,直接返回,不进行处理
    if (typeof seconds !== 'number')
        return seconds;
    // 参数为NaN类型,直接抛出异常
    if (isNaN(seconds))
        throw new Error(`formatTimeWithDetails方法的参数seconds必须时一个非NaN数字,现在的值为${seconds}`);
    const { day, hour, minute, decimal } = groupTimeDuration(seconds, 'day', 'minute', 2);
    let cost = '';
    const min = Math.ceil(minute + decimal);
    if (day > 0) { // 秒数多于1天
        cost = `${day}${hour}小时`;
        // 这里大概率是有bug的,从测试用例里能看出:formatTime(86400) => 1天0小时0分钟
        // 但为了不改变方法原有表现,先这样吧
        if (min > 0 || hour === 0)
            cost += `${min}分钟`;
    }
    else if (hour === 0) { // 秒数小于1小时
        cost = `${min}分钟`;
    }
    else { // 秒数介于1天和1分钟之间
        cost = `${hour}小时`;
        if (min > 0)
            cost += `${min}分钟`;
    }
    return cost;
};
/**
 * @function
 * @description 对原有时间字符串进行格式化
 * @param {String} str - 原字符串
 * @param {String} dateSeprator - 日期分隔符
 * @param {Boolean} keepSeconds - 是否保留秒数
 * @returns {String} 格式化后的文案
 */
const formatTimeStr = (str = '', dateSeprator = '.', keepSeconds = false) => {
    if (typeof str !== 'string' || str === '') {
        return '';
    }
    let s = str.replace(/-/g, dateSeprator).replace(//g, ':');
    // 不保留秒的时候,如果有两个冒号,截取第二个冒号之前的部分
    if (!keepSeconds && /[^:]*:[^:]*:/.test(s)) {
        const firstIndex = s.indexOf(':');
        const secondIndex = s.indexOf(':', firstIndex + 1);
        if (secondIndex > -1) {
            s = s.substring(0, secondIndex);
        }
    }
    return s;
};
/**
  * 格式化时间对象
  * @param {Date|Number} date  Date对象
  * @param {String}      fmt 目标格式,如:yyyy年MM月dd日,MM/dd/yyyy,yyyyMMdd,yyyy-MM-dd hh:mm:ss等
  * @returns {String}    格式化结果;异常情况下返回空串
  */
const formatDateTime = (date, fmt = 'yyyy-MM-dd hh:mm:ss') => {
    const dateObj = date instanceof Date ? date : new Date(date);
    if (isNaN(dateObj.getTime()))
        return '';
    const obj = {
        'M+': dateObj.getMonth() + 1,
        'd+': dateObj.getDate(),
        'h+': dateObj.getHours(),
        'm+': dateObj.getMinutes(),
        's+': dateObj.getSeconds(),
        S: dateObj.getMilliseconds(), // 毫秒
    };
    let dateStr = fmt || 'yyyy-MM-dd hh:mm:ss';
    if (/(y+)/.test(dateStr)) {
        dateStr = dateStr.replace(RegExp.$1, (`${dateObj.getFullYear()}`).substr(4 - RegExp.$1.length));
    }
    Object.entries(obj).forEach(([key, value]) => {
        if (new RegExp(`(${key})`).test(dateStr)) {
            dateStr = dateStr.replace(RegExp.$1, (RegExp.$1.length === 1) ? (value) : (`${value}`.padStart(2, '0')));
        }
    });
    return dateStr;
};
/**
 * @description 格式化时间戳为 yyyy-MM-dd, yyyy-MM-dd hh:mm, yyyy-MM-dd hh:mm:ss
 * @param {Date} date 日期
 * @param {Boolean} withTime 是否带时间
 * @param {Boolean} withSeconds 是否带秒数
 * @param {String} join 连字符
 * @returns {String} 格式化后的字符串,如 2021-03-18 或者 2021-03-18 10:11
 */
const dateToString = (date, withTime = false, withSeconds = false, join = '-') => {
    let fmt = `yyyy${join}MM${join}dd`;
    if (withTime)
        fmt += withSeconds ? ' hh:mm:ss' : ' hh:mm';
    return formatDateTime(date || new Date(), fmt);
};
/**
 * 将时间字符串转换为Date对象
 * @param {String} str  时间字符串,如:2022-03-24 20:02:05
1. 支持对日期完整但时间部分缺失的情况进行处理,如:
2. 2022-03-24 20:02 -> Date(1648123320000)
3. 2022-03-24 20:   -> Date(1648123200000)
4. 2022-03-24 20    -> Date(1648123200000)
5. 2022-03-24       -> Date(1648051200000)
6. 小于10的数字可以省略0:
7. 2022-3-24 20:2   -> Date(1648123320000)
8. 支持任意分割符,如:
9. 2022年03月24日 20时02分 -> Date(1648123320000)
10. 2022/03/24 20:02:05   -> Date(1648123325000)
 * @returns {Date} 时间对象;转换失败时返回null
 */
const parseDateTime = (str) => {
    const arr = new RegExp(/(\d{4})[^\d]+(\d{1,2})[^\d]+(\d{1,2})(.*)/, 'g').exec(str); // 分割日期和时间
    if (!(arr && arr.index > -1))
        return null; // 日期不合法,转换失败
    const timeArr = new RegExp(/[^\d]?(\d{1,2})([^\d]+(\d{1,2})([^\d]+(\d{1,2}))?)?/, 'g').exec(arr[4]); // 分割时间
    const [, year = 0, month = 0, day = 0] = arr; // 解析日期
    const [, hour = 0, , minute = 0, , second = 0] = timeArr || []; // 解析时间
    const date = new Date();
    date.setFullYear(year);
    // 解决js setMonth bug,在setMonth前先将day设置为1号,保证setMonth结果正确
    // https://www.google.com/search?q=js+setmonth+bug&oq=js+setMonth&aqs=chrome.1.69i57j0i512j0i8i30j0i5i30j69i61.4323j0j4&sourceid=chrome&ie=UTF-8
    date.setDate(1);
    date.setMonth(month - 1);
    date.setDate(day);
    date.setHours(hour);
    date.setMinutes(minute);
    date.setSeconds(second);
    date.setMilliseconds(0);
    return date;
};
export { groupTimeDuration, formatTimeDuration, formatDateTime, formatTime, formatTimeStr, formatTimeWithDetails, dateToString, parseDateTime, };
 

# Object groupTimeDuration

将时间段进行聚合计算,得出不同时间单位的计数值,例如:3601秒 -> { hour: 1, minute: 0, second: 1 },即:1小时0分1秒

参数

字段名 类型 说明 默认值 是否可选
seconds Number 时间段的总秒数 ---
maxUnit String 最大计数单位:second-秒,minute-分,hour-小时,day-天,month-月,year-年;默认 year ---
minUnit String 最小计数单位:second-秒,minute-分,hour-小时,day-天,month-月,year-年;默认 second ---
decimal Number 有不足最小单位数值的情况下,保留几位小数;默认 2 ---

返回值

类型 说明
Object result 以下是各计数单位下的数值:
Number result.year - 年; 当maxUnit < 'year' 时,此字段永远为0;此字段为-1时,表示 seconds 参数非法 或 minUnit > maxUnit等异常情况
Number result.month - 月; 当maxUnit < 'month' 时,此字段永远为0;
Number result.day - 天; 当maxUnit < 'day' 时,此字段永远为0;
Number result.hour - 小时;当maxUnit < 'hour' 时,此字段永远为0;
Number result.minute - 分钟;当maxUnit < 'minute' 时,此字段永远为0;
Number result.second - 秒;
Number result.decimal - 不足最小单位的部分,具体含义与 minUnit 参数相关;例如 minUnit = day 时,此字段表示不足1天的部分

# String formatTimeDuration

将时间段进行聚合计算并格式化,得出方便人阅读的文案,例如:3601秒 -> 1小时0分1秒

参数

字段名 类型 说明 默认值 是否可选
seconds Number 时间段的总秒数 ---
maxUnit String 最大计数单位:second-秒,minute-分,hour-小时,day-天,month-月,year-年;默认 year ---
minUnit String 最小计数单位:second-秒,minute-分,hour-小时,day-天,month-月,year-年;默认 second ---
decimal Number 有不足最小单位数值的情况下,保留几位小数;默认 2 ---

返回值

类型 说明
String

# String formatTime

格式化时间,>24小时的显示 【x天】;<1小时的显示 【x分钟】;<24小时&>1小时的显示 【x小时y分钟】

参数

字段名 类型 说明 默认值 是否可选
seconds Number 秒数 ---

返回值

类型 说明
String 格式化的时间

# String formatTimeWithDetails

将秒数格式化: 秒数多于1天显示【x天y小时z分钟】;秒数小于1小时【x分钟】;秒数介于1天和1小时之间显示【x小时y分钟】

参数

字段名 类型 说明 默认值 是否可选
seconds Number 秒数 ---

返回值

类型 说明
String 格式化后的文案

# String formatTimeStr

对原有时间字符串进行格式化

参数

字段名 类型 说明 默认值 是否可选
str String 原字符串 ---
dateSeprator String 日期分隔符 ---
keepSeconds Boolean 是否保留秒数 ---

返回值

类型 说明
String 格式化后的文案

# String formatDateTime

格式化时间对象

参数

字段名 类型 说明 默认值 是否可选
date Date|Number Date对象 ---
fmt String 目标格式,如:yyyy年MM月dd日,MM/dd/yyyy,yyyyMMdd,yyyy-MM-dd hh:mm:ss等 ---

返回值

类型 说明
String 格式化结果;异常情况下返回空串

# String dateToString

格式化时间戳为 yyyy-MM-dd, yyyy-MM-dd hh:mm, yyyy-MM-dd hh:mm:ss

参数

字段名 类型 说明 默认值 是否可选
date Date 日期 ---
withTime Boolean 是否带时间 ---
withSeconds Boolean 是否带秒数 ---
join String 连字符 ---

返回值

类型 说明
String 格式化后的字符串,如 2021-03-18 或者 2021-03-18 10:11

# Date parseDateTime

将时间字符串转换为Date对象

参数

字段名 类型 说明 默认值 是否可选
str String 时间字符串,如:2022-03-24 20:02:05
  1. 支持对日期完整但时间部分缺失的情况进行处理,如:
  2. 2022-03-24 20:02 -> Date(1648123320000)
  3. 2022-03-24 20: -> Date(1648123200000)
  4. 2022-03-24 20 -> Date(1648123200000)
  5. 2022-03-24 -> Date(1648051200000)
  6. 小于10的数字可以省略0:
  7. 2022-3-24 20:2 -> Date(1648123320000)
  8. 支持任意分割符,如:
  9. 2022年03月24日 20时02分 -> Date(1648123320000)
  10. 2022/03/24 20:02:05 -> Date(1648123325000) | --- | 否 |

返回值

类型 说明
Date 时间对象;转换失败时返回null
Last Updated: 4/25/2023, 19:37:42