@tmsfe/tms-core 0.0.1 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,286 @@
1
+ import Request from '../request';
2
+ import EventDispatcher from '../eventDispatcher';
3
+ import LocationBase from './base'; // eslint-disable-line
4
+ import { formatCityCode, formatPoi } from './utils'; // eslint-disable-line
5
+ import { WxPostionType } from '../types/object'; // eslint-disable-line
6
+
7
+ interface PostionType {
8
+ province: string,
9
+ cityCode: string,
10
+ cityName: string,
11
+ latitude: number,
12
+ longitude: number,
13
+ }
14
+
15
+ interface UserLocationType {
16
+ [key: string]: Promise<PostionType | void>
17
+ }
18
+
19
+ const request = new Request();
20
+ const event = new EventDispatcher();
21
+
22
+ const userLocationCache: UserLocationType = {}; // 获取位置缓存
23
+ // 用户手动选择的位置信息(切换城市后的位置信息)
24
+ let userLocation: PostionType | undefined;
25
+ // 用户所在的城市
26
+ let cityTheUserAt = '';
27
+ // 用户城市变化事件名
28
+ const CityChangeEventName = 'loc_city_changed';
29
+ const LocationType = 'gcj02'; // 获取经纬度时的坐标名称
30
+
31
+ /**
32
+ * @class Location
33
+ * @classdesc 基于LocationBase,封装业务侧位置的接口。 将用户经纬度转化为城市等展示信息
34
+ */
35
+ class Location extends LocationBase {
36
+ // 默认位置信息
37
+ public locForNoAuth = {
38
+ province: '广东省',
39
+ cityCode: '440300',
40
+ cityName: '深圳市',
41
+ latitude: 22.54286,
42
+ longitude: 114.05956,
43
+ };
44
+
45
+ /**
46
+ * 构造函数
47
+ */
48
+ constructor() { // eslint-disable-line
49
+ super();
50
+ }
51
+
52
+ /**
53
+ * 获取解析后的用户位置, 比如城市信息
54
+ * @param { number } lat 用户纬度信息
55
+ * @param { number } lng 用户经度信息
56
+ * @param { number } getPoi 位置
57
+ * @returns { Promise<PostionType | void> } 获取位置信息的promise
58
+ */
59
+ getPoiInfo(lat: number, lng: number, getPoi: number): Promise<PostionType | void> {
60
+ // 优先查询缓存中是否有位置信息,如果有直接返回
61
+ const cacheName = `${lat}-${lng}-${getPoi}`;
62
+
63
+ // @ts-ignore
64
+ if (userLocationCache[cacheName]) { // eslint-disable-line
65
+ return userLocationCache[cacheName];
66
+ }
67
+
68
+ userLocationCache[cacheName] = request.post('basic/lbs/decode', { lat, lng, getPoi }).then((res) => {
69
+ const { errCode, resData = {} } = res;
70
+
71
+ if (errCode === 0) {
72
+ const { result = {} } = resData;
73
+ const { ad_info: adInfo = {} } = result;
74
+
75
+ const loc = {
76
+ province: adInfo.province,
77
+ cityName: adInfo.city,
78
+ adCode: adInfo.adcode,
79
+ cityCode: formatCityCode(adInfo.city_code, adInfo.nation_code),
80
+ latitude: lat,
81
+ longitude: lng,
82
+ };
83
+
84
+ if (getPoi === 0) {
85
+ return loc;
86
+ }
87
+
88
+ loc.adCode = adInfo.adcode;
89
+ (loc as typeof loc & { poi: object }).poi = formatPoi(result);
90
+ return loc;
91
+ }
92
+
93
+ return Promise.reject(res);
94
+ })
95
+ .catch(err => Promise.reject(err));
96
+
97
+ return userLocationCache[cacheName];
98
+ }
99
+
100
+ /**
101
+ * @function
102
+ * @memberof Location
103
+ * @description 获取位置、城市信息
104
+ * @param {Boolean} showModalWhenCloseAuth 获取位置时,如果用户关闭了位置授权,是否弹窗提示用户打开授权
105
+ * @param {String} type 坐标类型
106
+ * @param {String} content 弹窗内容
107
+ * @param {Number} getPoi 是否获取详细poi信息, 0 - 不获取(不返回poi字段), 1 - 获取(返回poi字段)
108
+ * @returns {Promise<POI|ERR>} 返回对象
109
+ * @example
110
+ * <caption>POI类型示例</caption>
111
+ * {
112
+ * cityName: '北京市',
113
+ * cityCode: '100100',
114
+ * province: '北京市',
115
+ * latitude: 325.255333,
116
+ * longitude: 116.2545454,
117
+ * adCode: 1212144,
118
+ * poi: {
119
+ * id: '1114545554511',
120
+ * title: '腾讯北京总部大厦',
121
+ * address : '北京市海淀区东北旺西路',
122
+ * }
123
+ * }
124
+ * @example
125
+ * <caption>ERR类型示例</caption>
126
+ * {
127
+ * err,
128
+ * unAuthed: true,
129
+ * locationScopeStatus: false
130
+ * }
131
+ */
132
+ getLocationDetail(showModalWhenCloseAuth = false, type = LocationType, content = '', getPoi = 0) {
133
+ return this.getLocation(showModalWhenCloseAuth, type, content)
134
+ .then((res: WxPostionType) => this.getPoiInfo(res.latitude, res.longitude, getPoi))
135
+ .catch(err => Promise.reject(err));
136
+ }
137
+
138
+ /**
139
+ * @description 以静默方式获取用户详细位置(经纬度,poi信息, 省市信息),说明如下:<br>
140
+ * 1、从未授权情况下 | 用户删除小程序之后调用该方法不会弹微信自带的授权弹窗;<br>
141
+ * 2、拒绝授权后,调用该方法不会弹窗提示用户去授权(wx.showModal);<br>
142
+ * @memberof Location
143
+ * @param {Number} getPoi 是否获取详细poi信息, 0 - 不获取(不返回poi字段), 1 - 获取(返回poi字段)
144
+ * @param {String} type 非必填。坐标类型,默认'gcj02'
145
+ * @returns {Promise<LOC|ERR>} 返回对象
146
+ * @example
147
+ * <caption>LOC类型示例</caption>
148
+ * {
149
+ * cityName: '北京市',
150
+ * cityCode: '100100',
151
+ * latitude: 325.255333,
152
+ * longitude: 116.2545454,
153
+ * adCode: 1212144,
154
+ * poi: {
155
+ * id: '1114545554511',
156
+ * title: '腾讯北京总部大厦',
157
+ * address : '北京市海淀区东北旺西路',
158
+ * }
159
+ * }
160
+ * @example
161
+ * <caption>ERR类型示例</caption>
162
+ * {
163
+ * err,
164
+ * unAuthed: true,
165
+ * locationScopeStatus: false
166
+ * }
167
+ */
168
+ getLocationDetailSilent(getPoi = 0, type = 'gcj02') {
169
+ return this.getLocationSilent(type)
170
+ .then((res: any) => this.getPoiInfo(res.latitude, res.longitude, getPoi))
171
+ .catch(err => Promise.reject(err));
172
+ }
173
+
174
+ /**
175
+ * @description 获取用户手动选择的位置信息
176
+ * @memberof Location
177
+ * @returns {Null|LOC} 返回用户位置信息
178
+ * @example
179
+ * <caption>LOC类型示例</caption>
180
+ * {
181
+ * province: '北京市',
182
+ * cityName: '北京市',
183
+ * cityCode: '100100',
184
+ * latitude: 325.255333,
185
+ * longitude: 116.2545454,
186
+ * adCode: 1212144,
187
+ * }
188
+ */
189
+ getUserLocation(): PostionType | undefined {
190
+ return userLocation;
191
+ }
192
+
193
+ /**
194
+ * @description 设置用户位置(小程序生命周期内有效)
195
+ * @memberof Location
196
+ * @param {Object} loc 用户手动选择的位置
197
+ * @params {String} loc.province 省份
198
+ * @params {String} loc.cityName 城市
199
+ * @params {String} loc.cityCode citycode
200
+ * @params {Number} loc.latitude 纬度
201
+ * @params {Number} loc.longitude 经度
202
+ * @params {Number} loc.adCode adCode
203
+ * @returns {Void} 无返回值
204
+ * @example
205
+ * <caption>loc类型示例</caption>
206
+ * {
207
+ * province: '北京市',
208
+ * cityName: '北京市',
209
+ * cityCode: '100100',
210
+ * latitude: 325.255333,
211
+ * longitude: 116.2545454,
212
+ * adCode: 1212144,
213
+ * }
214
+ */
215
+ setUserLocation(loc: PostionType) {
216
+ if (!loc || typeof loc !== 'object') {
217
+ return;
218
+ }
219
+
220
+ // 如果城市发生变化,派发事件
221
+ const { cityName: curCityName } = loc;
222
+ if (curCityName && (cityTheUserAt !== curCityName)) {
223
+ cityTheUserAt = curCityName;
224
+ userLocation = loc;
225
+ event.dispatch({ type: CityChangeEventName });
226
+ }
227
+ }
228
+
229
+ /**
230
+ * @description 获取用户选择的城市位置信息 或者 用户真实的位置信息(这个方法说明有问题)
231
+ * @memberof Location
232
+ * @param {Boolean} showModalWhenCloseAuth 没有授权时是否展示授权弹窗
233
+ * @returns {Promise} 用户位置信息
234
+ */
235
+ async getMergedLocation(showModalWhenCloseAuth = true): Promise<void | PostionType> {
236
+ const userLocatioin = this.getUserLocation();
237
+ // 优先用户选择的城市
238
+ if (userLocatioin) {
239
+ return userLocatioin;
240
+ }
241
+
242
+ return await this.getLocationDetail(showModalWhenCloseAuth, LocationType);
243
+ }
244
+
245
+ /**
246
+ * @memberof Location
247
+ * @description 监听用户城市变化(如: 北京市->深圳市)
248
+ * @param {Function} cb 监听事件回调函数
249
+ * @returns {void}
250
+ */
251
+ onCityChange(cb: () => void) {
252
+ if (cb && typeof cb === 'function') {
253
+ event.bind(CityChangeEventName, cb);
254
+ }
255
+ }
256
+
257
+ /**
258
+ * @memberof Location
259
+ * @description 获取路线规划
260
+ * @param {String} to 目的地坐标,格式:lat,lng
261
+ * @param {String} depart 出发地坐标,格式:lat,lng
262
+ * @param {String} mode 出行方式, driving 驾车[默认]
263
+ * @returns {Object} result.routes 返回数据 [{}, ...]
264
+ */
265
+ async getRoute(to: string, depart: string, mode = 'driving') {
266
+ let from = depart;
267
+ const coorReg = new RegExp(/(\d+\.\d{6,}),(\d+\.\d{6,})/);
268
+ if (!coorReg.test(to)) throw Error('目的地参数格式错误');
269
+ if (!coorReg.test(from)) { // 出发地缺省使用当前位置
270
+ const { latitude, longitude } = (await this.getMergedLocation() as PostionType);
271
+ from = `${latitude},${longitude}`;
272
+ }
273
+ return request.post('basic/lbs/direction', { from, to, mode });
274
+ }
275
+ }
276
+
277
+ // 因为在构造函数中会用到wx的api,所以使用到时才实例化
278
+ let instance: Location;
279
+ function getLocInstance(): Location {
280
+ if (!instance) {
281
+ instance = new Location();
282
+ }
283
+ return instance;
284
+ }
285
+
286
+ export default getLocInstance;
@@ -0,0 +1,168 @@
1
+ import EventDispatcher from '../eventDispatcher';
2
+
3
+ const event = new EventDispatcher();
4
+
5
+ // 获取位置状态 -- true: 成功 false: 失败
6
+ let getLocationStatus = true; // eslint-disable-line
7
+
8
+ const LocationScopeKey = 'scope.userLocation';
9
+
10
+ // 获取授权设置promise
11
+ let getSettingPromise: Promise<any> | null;
12
+
13
+ const EventName = 'loc_status_changed';
14
+
15
+
16
+ /**
17
+ * @param {string} scopeKey 授权项
18
+ * @returns {promise} 授权页操作状态
19
+ */
20
+ export function openSetting(scopeKey = LocationScopeKey) {
21
+ return new Promise((resolve, reject) => {
22
+ // 打开设置页
23
+ wx.openSetting({
24
+ success: (res) => {
25
+ // 从authSetting中获取用户的授权结果
26
+ const { authSetting } = res;
27
+ const authResult = (authSetting as any)[scopeKey];
28
+ // 已经打开授权
29
+ if (authResult) {
30
+ resolve(res);
31
+ return;
32
+ }
33
+
34
+ // 未打开授权
35
+ reject(res);
36
+ },
37
+ fail: reject,
38
+ });
39
+ });
40
+ }
41
+
42
+ /**
43
+ * @description 更新获取位置状态
44
+ * @param {Boolean} status 成功true或失败false
45
+ * @returns {Promise<void>} 用户是否授权
46
+ */
47
+ export async function updateLocStatus(status: boolean): Promise<void> {
48
+ if (status && !getLocationStatus) {
49
+ let isAuthed = false;
50
+
51
+ try {
52
+ isAuthed = await getSetting();
53
+ getLocationStatus = true;
54
+ } catch (e) {
55
+ if (isAuthed !== false) {
56
+ event.dispatch({ type: EventName });
57
+ }
58
+
59
+ const timer = setTimeout(() => {
60
+ updateLocStatus(status);
61
+ clearTimeout(timer);
62
+ }, 200);
63
+ }
64
+ } else {
65
+ getLocationStatus = status;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * 获取用户授权信息
71
+ * @param { string } scopeKey 是否授权过位置信息
72
+ * @returns { Promise } 用户授权对象
73
+ */
74
+ export function getSetting(scopeKey: string = LocationScopeKey) {
75
+ if (getSettingPromise) { // eslint-disable-line
76
+ return getSettingPromise;
77
+ }
78
+
79
+ getSettingPromise = new Promise((resolve, reject) => {
80
+ wx.getSetting({
81
+ success: ({ authSetting = {} }) => {
82
+ resolve((authSetting as any)[scopeKey]);
83
+ getSettingPromise = null;
84
+ },
85
+ fail: (err: { errMsg: string }) => {
86
+ getSettingPromise = null;
87
+
88
+ const { errMsg = '' } = err || {};
89
+ // wx.getSetting接口没有响应时errMsg
90
+ const reason = 'getSetting:fail data no response';
91
+ // 部分机型上,wx.getSetting接口没有响应,此时,当做位置开关是打开的来处理
92
+ if (errMsg.indexOf(reason) > -1) {
93
+ resolve(true);
94
+ return;
95
+ }
96
+
97
+ reject(err);
98
+ },
99
+ });
100
+ });
101
+
102
+ return getSettingPromise;
103
+ }
104
+
105
+ /**
106
+ * getTextByReason 获取提示语文案
107
+ * @param {*} locationEnabled 是否开启位置授权
108
+ * @param {*} isSysAndWechatAllowedGetLoc 是否开启微信位置授权
109
+ * @returns {object} 提示语文案
110
+ */
111
+ export function getTextByReason(locationEnabled: boolean, isSysAndWechatAllowedGetLoc: boolean) {
112
+ const authLevelName = locationEnabled ? '微信' : '系统';
113
+ const confirmText = isSysAndWechatAllowedGetLoc ? '去开启' : '好的';
114
+
115
+ return { authLevelName, confirmText };
116
+ };
117
+
118
+ /**
119
+ * @function
120
+ * @description 获取设备系统信息
121
+ * @returns {Object} 设备系统信息
122
+ */
123
+ export function getSystemInfo(): WechatMiniprogram.SystemInfo | undefined {
124
+ try {
125
+ const sys: WechatMiniprogram.SystemInfo = wx.getSystemInfoSync();
126
+ // 方便开发时调试
127
+ if (sys.platform === 'devtools') {
128
+ sys.locationEnabled = true;
129
+ sys.locationAuthorized = true;
130
+ }
131
+ return sys;
132
+ } catch (err) { }
133
+ };
134
+
135
+
136
+ /**
137
+ * @function
138
+ * @description 格式化城市编码
139
+ * @param {String} cityCode 城市编码,以国家编码开始
140
+ * @param {String} nationCode 国家编码
141
+ * @returns {String} 去掉国家编码后的城市编码
142
+ */
143
+ export function formatCityCode(cityCode: string, nationCode: string) {
144
+ let code = cityCode;
145
+ if (cityCode.startsWith(nationCode)) {
146
+ code = cityCode.substr(nationCode.length);
147
+ }
148
+
149
+ return code;
150
+ };
151
+
152
+ /**
153
+ * 格式化poi数据
154
+ * @param {Object} geo 逆地址解析接口返回的数据
155
+ * @returns {object} 格式化后的poi结果
156
+ */
157
+ export function formatPoi(geo: any) {
158
+ const { pois = [], address_reference: addressRefer } = geo;
159
+ if (pois.length) {
160
+ return pois[0];
161
+ }
162
+
163
+ if (addressRefer?.landmark_l2) {
164
+ return addressRefer.landmark_l2;
165
+ }
166
+
167
+ return {};
168
+ };
package/src/log.js ADDED
@@ -0,0 +1,171 @@
1
+ /**
2
+ * 本文件主要负责在小程序中日志打印功能,包含本地日志及实时日志. 主要做了两件事:
3
+ * 1、参数序列化处理;支持传递任意多个参数,并对类型为对象的参数进行字符串序列化处理(避免打印出来是'[Object Object]'的格式);
4
+ * 2、低版本兼容;
5
+ */
6
+
7
+ // 低版本不支持getLogManager或者getRealtimeLogManager时,用ManagerForLowerVersionLib来兼容
8
+ const ManagerForLowerVersionLib = {
9
+ debug: () => {},
10
+ info: () => {},
11
+ log: () => {},
12
+ warn: () => {},
13
+ error: () => {},
14
+ addFilterMsg: () => {},
15
+ setFilterMsg: () => {},
16
+ };
17
+
18
+ // 小程序基础库2.7.1版本以上支持,所以需要兼容性处理
19
+ let logInstance = null;
20
+ let rtLogInstance = null;
21
+
22
+ function getLogInstance() {
23
+ if (logInstance === null) {
24
+ logInstance = wx.getLogManager ? wx.getLogManager() : ManagerForLowerVersionLib;
25
+ }
26
+ return logInstance;
27
+ }
28
+
29
+ function getRTLogInstance() {
30
+ if (rtLogInstance === null) {
31
+ rtLogInstance = wx.getRealtimeLogManager ? wx.getRealtimeLogManager() : ManagerForLowerVersionLib;
32
+ }
33
+ return rtLogInstance;
34
+ }
35
+
36
+ /**
37
+ * 参数中有对象类型的,将其转换为字符串类型,以便查看
38
+ * @param {Array<Any>} params 需要格式化的数据
39
+ * @returns {Array<String>} 字符串序列化后的数据
40
+ */
41
+ const format = params => params.map(param => (typeof param === 'string' ? param : JSON.stringify(param)));
42
+
43
+ /**
44
+ * @namespace LOG
45
+ * @description 普通日志管理器,将日志记录在小程序日志文件中,用户上传后,可以在小程序后台-反馈管理中看到
46
+ */
47
+ const LOG = {
48
+ /**
49
+ * @description 写debug日志
50
+ * @param {...Any} params 需要打印的数据,支持任意多个
51
+ * @returns {Void} 无返回值
52
+ */
53
+ debug(...params) {
54
+ getLogInstance().debug(...format(params));
55
+ },
56
+
57
+ /**
58
+ * @description 写info日志
59
+ * @param {...Any} params 需要打印的数据,支持任意多个
60
+ * @returns {Void} 无返回值
61
+ */
62
+ info(...params) {
63
+ getLogInstance().info(...format(params));
64
+ },
65
+
66
+ /**
67
+ * @description 写log日志
68
+ * @param {...Any} params 需要打印的数据,支持任意多个
69
+ * @returns {Void} 无返回值
70
+ */
71
+ log(...params) {
72
+ getLogInstance().log(...format(params));
73
+ },
74
+
75
+ /**
76
+ * @description 写warn日志
77
+ * @param {...Any} params 需要打印的数据,支持任意多个
78
+ * @returns {Void} 无返回值
79
+ */
80
+ warn(...params) {
81
+ getLogInstance().warn(...format(params));
82
+ },
83
+
84
+ /**
85
+ * @description 写warn日志. LogManager并没有error方法,为了兼容旧代码,所以声明一个error方法
86
+ * @param {...Any} params 需要打印的数据,支持任意多个
87
+ * @returns {Void} 无返回值
88
+ */
89
+ error(...params) {
90
+ LOG.warn(...params);
91
+ },
92
+ };
93
+
94
+ /**
95
+ * @namespace RTLOG
96
+ * @description 实时日志,将日志实时上传至小程序后台-开发-运维中心-实时日志,方便快速排查漏洞,定位问题
97
+ */
98
+ const RTLOG = {
99
+ /**
100
+ * @description 写info日志
101
+ * @param {...Any} params 需要打印的数据,支持任意多个
102
+ * @returns {Void} 无返回值
103
+ */
104
+ info(...params) {
105
+ getRTLogInstance().info(...format(params));
106
+ },
107
+
108
+ /**
109
+ * @description 写warn日志
110
+ * @param {...Any} params 需要打印的数据,支持任意多个
111
+ * @returns {Void} 无返回值
112
+ */
113
+ warn(...params) {
114
+ getRTLogInstance().warn(...format(params));
115
+ },
116
+
117
+ /**
118
+ * @description 写error日志
119
+ * @param {...Any} params 需要打印的数据,支持任意多个
120
+ * @returns {Void} 无返回值
121
+ */
122
+ error(...params) {
123
+ getRTLogInstance().error(...format(params));
124
+ },
125
+
126
+ /**
127
+ * @description 添加过滤关键字
128
+ * @param {String} msg 关键字
129
+ * @returns {Void} 无返回值
130
+ */
131
+ addFilterMsg(msg) {
132
+ getRTLogInstance().addFilterMsg(msg);
133
+ },
134
+
135
+ /**
136
+ * @description 设置过滤关键字
137
+ * @param {String} msg 关键字
138
+ * @returns {Void} 无返回值
139
+ */
140
+ setFilterMsg(msg) {
141
+ getRTLogInstance().setFilterMsg(msg);
142
+ },
143
+ };
144
+
145
+ /**
146
+ * @description 获取日志管理器对象,该对象提供的方法同wx.getLogManager()提供的方法,详见微信文档
147
+ * @returns {Object} [LOG](#namespace-log)
148
+ * @example
149
+ * const logger = getLogManager();
150
+ * logger.log(1, 'str', { a: 1 }, ...);
151
+ * logger.info(1, 'str', { a: 1 }, ...);
152
+ * logger.debug(1, 'str', { a: 1 }, ...);
153
+ * logger.awrn(1, 'str', { a: 1 }, ...);
154
+ */
155
+ const getLogManager = () => LOG;
156
+
157
+ /**
158
+ * @description 获取实时日志管理器对象,该对象提供的方法同wx.getRealtimeLogManager()提供的方法,详见微信文档
159
+ * @returns {Object} [RTLOG](#namespace-rtlog)
160
+ * @example
161
+ * const logger = getRealtimeLogManager();
162
+ * logger.info(1, 'str', { a: 1 }, ...);
163
+ * logger.warn(1, 'str', { a: 1 }, ...);
164
+ * logger.error(1, 'str', { a: 1 }, ...);
165
+ */
166
+ const getRealtimeLogManager = () => RTLOG;
167
+
168
+ export {
169
+ getLogManager,
170
+ getRealtimeLogManager,
171
+ };
@@ -0,0 +1,16 @@
1
+ import Log from './log';
2
+
3
+ describe('Log', () => {
4
+ beforeAll(() => {
5
+ });
6
+
7
+ it('Log.getLogInstance test', () => {
8
+ const logger = Log.__get__('getLogInstance')();
9
+ expect(logger).toBeInstanceOf(Object);
10
+ });
11
+
12
+ it('Log.getRTLogInstance test', () => {
13
+ const logger = Log.__get__('getRTLogInstance')();
14
+ expect(logger).toBeInstanceOf(Object);
15
+ });
16
+ });