@tmsfe/tms-core 0.0.40 → 0.0.43
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.
- package/package.json +1 -1
- package/src/index-proxy.js +3 -1
- package/src/index.js +6 -1
- package/src/mpInfo.js +16 -0
- package/src/report/formatV1.ts +5 -4
- package/src/report/formatV2.ts +4 -3
- package/src/report/helper.ts +53 -48
- package/src/report/proxy/component.ts +42 -0
- package/src/report/proxy/helper.ts +133 -0
- package/src/report/proxy/index.ts +20 -0
- package/src/report/proxy/page.ts +80 -0
- package/src/report/types.ts +2 -1
package/package.json
CHANGED
package/src/index-proxy.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import syncApi from './syncfnmanager';
|
|
9
|
+
import AutoReport from './report/proxy/index';
|
|
9
10
|
import md5 from './md5';
|
|
10
11
|
import { rpxToPx } from './rpx';
|
|
11
12
|
import { serialize } from './objUtils';
|
|
@@ -39,6 +40,7 @@ function invoke(obj, funcName, args) {
|
|
|
39
40
|
function initProxy(appObj, options) {
|
|
40
41
|
app = appObj;
|
|
41
42
|
initOptions = options;
|
|
43
|
+
AutoReport.init();
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
function awaitTMS() {
|
|
@@ -61,7 +63,7 @@ const asyncFuncNames = [
|
|
|
61
63
|
// 这行是tms-runtime的
|
|
62
64
|
'getPhone', 'login', 'getLoginInfo', 'getOpenId', 'getMycarPubOpenId', 'getSinanPubOpenId',
|
|
63
65
|
// tms-core
|
|
64
|
-
'setAuthInfo', 'getConfig', 'navigateToWebview', 'callCloudFunc',
|
|
66
|
+
'setAuthInfo', 'getConfig', 'navigateToWebview', 'callCloudFunc', 'getEncryptUserInfo',
|
|
65
67
|
'setUserLocation', 'getUserLocation', 'getMpOpenId', 'getOuterOpenId',
|
|
66
68
|
];
|
|
67
69
|
asyncFuncNames.forEach((funcName) => {
|
package/src/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Request from './request';
|
|
2
|
+
import AutoReport from './report/proxy/index';
|
|
2
3
|
import Reporter from './report/index';
|
|
3
4
|
import { getConfig } from './config';
|
|
4
5
|
import syncApi from './syncfnmanager';
|
|
@@ -36,7 +37,7 @@ import {
|
|
|
36
37
|
} from './ipxHelper';
|
|
37
38
|
import getLocInstance from './location/index';
|
|
38
39
|
import LocationBase from './location/base';
|
|
39
|
-
import { getMpOpenId, getOuterOpenId } from './mpInfo';
|
|
40
|
+
import { getMpOpenId, getOuterOpenId, getEncryptUserInfo } from './mpInfo';
|
|
40
41
|
import * as storage from './storage';
|
|
41
42
|
|
|
42
43
|
/**
|
|
@@ -101,6 +102,7 @@ const init = (options = {}) => {
|
|
|
101
102
|
setEnvInfo(envInfo);
|
|
102
103
|
setAppPagePaths(appPagePaths, homePage);
|
|
103
104
|
Reporter.init(envInfo);
|
|
105
|
+
AutoReport.init();
|
|
104
106
|
Request.defaultHost = defaultHost;
|
|
105
107
|
// 初始化云环境
|
|
106
108
|
wx.cloud.init({ env: cloudEnvId });
|
|
@@ -171,6 +173,9 @@ const api = {
|
|
|
171
173
|
getMpOpenId, // 变更为 getOuterOpenId
|
|
172
174
|
getOuterOpenId,
|
|
173
175
|
|
|
176
|
+
/* 获取加密的用户信息 */
|
|
177
|
+
getEncryptUserInfo,
|
|
178
|
+
|
|
174
179
|
/** rpx转px */
|
|
175
180
|
rpxToPx,
|
|
176
181
|
|
package/src/mpInfo.js
CHANGED
|
@@ -32,7 +32,23 @@ async function getOuterOpenId(apiKey) {
|
|
|
32
32
|
return openId;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
async function getEncryptUserInfo(mpId = '', queryTypes = '') {
|
|
36
|
+
if (!mpId) return Promise.reject('缺少服务标志mpId');
|
|
37
|
+
if (!queryTypes) return Promise.reject('缺少需要加密的用户信息字段描述');
|
|
38
|
+
return new Request().get('user/info/encrypt', { mpId, queryTypes })
|
|
39
|
+
.then((res) => {
|
|
40
|
+
const { resData, errCode, errMsg } = res || {};
|
|
41
|
+
if (errCode === 0) {
|
|
42
|
+
// 请求成功
|
|
43
|
+
return resData;
|
|
44
|
+
}
|
|
45
|
+
return Promise.reject(errMsg);
|
|
46
|
+
})
|
|
47
|
+
.catch(e => Promise.reject('请求发生错误,请重试'));
|
|
48
|
+
}
|
|
49
|
+
|
|
35
50
|
export {
|
|
36
51
|
getMpOpenId,
|
|
37
52
|
getOuterOpenId,
|
|
53
|
+
getEncryptUserInfo,
|
|
38
54
|
};
|
package/src/report/formatV1.ts
CHANGED
|
@@ -9,14 +9,15 @@ import helper from './helper';
|
|
|
9
9
|
* 获取埋点的基础字段
|
|
10
10
|
*/
|
|
11
11
|
function getBaseData(deviceData: IDeviceData): DataItem[] {
|
|
12
|
-
const arr = new Array<string>(helper.dataArrLen);
|
|
13
12
|
const { networkType, location } = deviceData;
|
|
14
13
|
const { appVersion, client } = helper.getInitOptions();
|
|
15
14
|
const { host } = helper.getSystemInfo();
|
|
15
|
+
const arr = new Array<string>(helper.dataArrLen);
|
|
16
16
|
// todo: 如何区分新旧埋点?新:f20不为空,旧:f20为空
|
|
17
17
|
// ++++++++++++++++++++++++++字段列表++++++++++++++++++++++++++
|
|
18
18
|
// 0: log_time,日志入库时间
|
|
19
|
-
// 1: access_time
|
|
19
|
+
// 1: access_time,埋点触发时间
|
|
20
|
+
arr[1] = Date.now().toString();
|
|
20
21
|
// 2: user_ip,前端无需赋值
|
|
21
22
|
// 3: qimei,灯塔中的用户ID
|
|
22
23
|
// 4: imei,Android手机的imei IOS系统中的idfv 车联网中的wecarid
|
|
@@ -50,7 +51,7 @@ function getBaseData(deviceData: IDeviceData): DataItem[] {
|
|
|
50
51
|
arr[29] = helper.getAppScene();
|
|
51
52
|
// 33: f33,系统信息
|
|
52
53
|
arr[33] = helper.getSystemInfoString();
|
|
53
|
-
// 36: f36
|
|
54
|
+
// 36: f36,小程序启动时的url和参数
|
|
54
55
|
arr[36] = helper.getLaunchOptionsString();
|
|
55
56
|
// --------------------------字段列表--------------------------
|
|
56
57
|
return arr;
|
|
@@ -84,7 +85,7 @@ function formatData(data: IOldParams): Promise<DataItem[]> {
|
|
|
84
85
|
* 格式化快速上报埋点数据,不依赖用户位置
|
|
85
86
|
*/
|
|
86
87
|
function formatFastData(data: IOldParams): DataItem[] {
|
|
87
|
-
return jointData(data, helper.
|
|
88
|
+
return jointData(data, helper.getCacheDeviceData());
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
export default {
|
package/src/report/formatV2.ts
CHANGED
|
@@ -16,7 +16,8 @@ function getBaseData(deviceData: IDeviceData): { arr: DataItem[], nextIndex: num
|
|
|
16
16
|
// todo: 如何区分新旧埋点?新:f20不为空,旧:f20为空
|
|
17
17
|
// ++++++++++++++++++++++++++字段列表++++++++++++++++++++++++++
|
|
18
18
|
// 0: log_time,日志入库时间
|
|
19
|
-
// 1: access_time
|
|
19
|
+
// 1: access_time,埋点触发时间
|
|
20
|
+
arr[1] = Date.now().toString();
|
|
20
21
|
// 2: user_ip,前端无需赋值
|
|
21
22
|
// 3: qimei,灯塔中的用户ID
|
|
22
23
|
// 4: imei,Android手机的imei IOS系统中的idfv 车联网中的wecarid
|
|
@@ -47,7 +48,7 @@ function getBaseData(deviceData: IDeviceData): { arr: DataItem[], nextIndex: num
|
|
|
47
48
|
arr[20] = client;
|
|
48
49
|
// 21: f21,小程序场景值
|
|
49
50
|
arr[21] = helper.getAppScene();
|
|
50
|
-
// 22: f22
|
|
51
|
+
// 22: f22,小程序启动时的url和参数
|
|
51
52
|
arr[22] = helper.getLaunchOptionsString();
|
|
52
53
|
// 23: f23,当前页面的url
|
|
53
54
|
arr[23] = page.route;
|
|
@@ -92,7 +93,7 @@ function formatData(data: any[]): Promise<DataItem[]> {
|
|
|
92
93
|
* 格式化快速上报埋点数据,不依赖用户位置
|
|
93
94
|
*/
|
|
94
95
|
function formatFastData(data: any[]): DataItem[] {
|
|
95
|
-
return jointData(data, helper.
|
|
96
|
+
return jointData(data, helper.getCacheDeviceData());
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
export default {
|
package/src/report/helper.ts
CHANGED
|
@@ -18,6 +18,16 @@ let initOptions: IInitOptions;
|
|
|
18
18
|
function init(options: IInitOptions): void {
|
|
19
19
|
if (!initOptions) {
|
|
20
20
|
initOptions = options;
|
|
21
|
+
|
|
22
|
+
// 从上次缓存中读取,方便首次Report可以先带上
|
|
23
|
+
const loc = wx.getStorageSync('home.location_city') || wx.getStorageSync('home.city');
|
|
24
|
+
deviceData = {
|
|
25
|
+
networkType: '',
|
|
26
|
+
location: {
|
|
27
|
+
province: loc?.province as string,
|
|
28
|
+
cityName: loc?.cityName as string,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
21
31
|
}
|
|
22
32
|
}
|
|
23
33
|
|
|
@@ -37,7 +47,7 @@ function getSystemInfo(): ISystemInfo {
|
|
|
37
47
|
if (systemInfo === null) {
|
|
38
48
|
const system = syncApi.getSystemInfoSync() as any;
|
|
39
49
|
// eslint-disable-next-line
|
|
40
|
-
const { model = '', version: wxVersion = '', platform = '', SDKVersion = '', host
|
|
50
|
+
const { model = '', version: wxVersion = '', platform = '', SDKVersion = '', host } = system;
|
|
41
51
|
systemInfo = { model, wxVersion, platform, SDKVersion, host };
|
|
42
52
|
}
|
|
43
53
|
return systemInfo;
|
|
@@ -60,6 +70,9 @@ function getSystemInfoString(): string {
|
|
|
60
70
|
* @param value
|
|
61
71
|
*/
|
|
62
72
|
function convert2String(value: any): string {
|
|
73
|
+
if (value === null || value === undefined) {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
63
76
|
const type = typeof value;
|
|
64
77
|
if (type === 'string') {
|
|
65
78
|
return value;
|
|
@@ -95,8 +108,8 @@ let launchOptions: string | null = null;
|
|
|
95
108
|
*/
|
|
96
109
|
function getLaunchOptionsString(): string {
|
|
97
110
|
if (launchOptions === null) {
|
|
98
|
-
const obj = syncApi.getLaunchOptionsSync()
|
|
99
|
-
launchOptions =
|
|
111
|
+
const obj = syncApi.getLaunchOptionsSync();
|
|
112
|
+
launchOptions = JSON.stringify(obj);
|
|
100
113
|
}
|
|
101
114
|
return launchOptions;
|
|
102
115
|
}
|
|
@@ -106,7 +119,7 @@ function getLaunchOptionsString(): string {
|
|
|
106
119
|
*/
|
|
107
120
|
function getLaunchFrom(): string {
|
|
108
121
|
const obj = syncApi.getLaunchOptionsSync() as any;
|
|
109
|
-
return obj.query?.from
|
|
122
|
+
return obj.query?.from;
|
|
110
123
|
}
|
|
111
124
|
|
|
112
125
|
/**
|
|
@@ -141,46 +154,8 @@ function canReport(): boolean {
|
|
|
141
154
|
*/
|
|
142
155
|
const dataArrLen = 41;
|
|
143
156
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
location: {
|
|
147
|
-
province: null,
|
|
148
|
-
cityName: null,
|
|
149
|
-
},
|
|
150
|
-
} as any as IDeviceData;
|
|
151
|
-
|
|
152
|
-
let networkPromise: Promise<string> | null = null;
|
|
153
|
-
|
|
154
|
-
function getNetworkType(): Promise<string> {
|
|
155
|
-
if (networkPromise === null) {
|
|
156
|
-
networkPromise = new Promise((resolve) => {
|
|
157
|
-
wx.getNetworkType({
|
|
158
|
-
success: (res: any) => resolve(res.networkType),
|
|
159
|
-
fail: () => resolve('unknown'),
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
return networkPromise;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
let locPromise: Promise<ILocation> | null = null;
|
|
167
|
-
|
|
168
|
-
function getLocation(): Promise<ILocation> {
|
|
169
|
-
if (locPromise === null) {
|
|
170
|
-
locPromise = new Promise<ILocation>((resolve) => {
|
|
171
|
-
const LOC = getTms().getLocationManager();
|
|
172
|
-
LOC.getIpLocation()
|
|
173
|
-
.then((res: any) => {
|
|
174
|
-
const ad = res.ad_info;
|
|
175
|
-
resolve({ province: ad.province, cityName: ad.city });
|
|
176
|
-
})
|
|
177
|
-
.catch(() => {
|
|
178
|
-
resolve({ province: '', cityName: '' });
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
return locPromise;
|
|
183
|
-
}
|
|
157
|
+
// 缓存设备等信息,方便FastReport使用
|
|
158
|
+
let deviceData: IDeviceData;
|
|
184
159
|
|
|
185
160
|
let devicePromise: Promise<IDeviceData> | null = null;
|
|
186
161
|
|
|
@@ -190,15 +165,45 @@ let devicePromise: Promise<IDeviceData> | null = null;
|
|
|
190
165
|
function getDeviceData(): Promise<IDeviceData> {
|
|
191
166
|
if (devicePromise === null) {
|
|
192
167
|
devicePromise = new Promise<IDeviceData>((resolve) => {
|
|
193
|
-
Promise
|
|
194
|
-
|
|
195
|
-
|
|
168
|
+
const networkPromise = new Promise<void>((resolve) => {
|
|
169
|
+
wx.getNetworkType({
|
|
170
|
+
success: (res: any) => {
|
|
171
|
+
deviceData.networkType = res.networkType;
|
|
172
|
+
resolve();
|
|
173
|
+
},
|
|
174
|
+
fail: () => resolve(),
|
|
175
|
+
});
|
|
196
176
|
});
|
|
177
|
+
|
|
178
|
+
const locPromise = new Promise<void>((resolve) => {
|
|
179
|
+
// 首次如果有缓存则先用缓存,请求后再更新给下次用
|
|
180
|
+
if (deviceData.location.province) {
|
|
181
|
+
resolve();
|
|
182
|
+
}
|
|
183
|
+
const LOC = getTms().getLocationManager();
|
|
184
|
+
LOC.getIpLocation()
|
|
185
|
+
.then((res: any) => {
|
|
186
|
+
const ad = res.ad_info;
|
|
187
|
+
deviceData.location.province = ad.province;
|
|
188
|
+
deviceData.location.cityName = ad.city;
|
|
189
|
+
resolve();
|
|
190
|
+
})
|
|
191
|
+
.catch(() => resolve());
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
Promise.all([networkPromise, locPromise]).then(() => resolve(deviceData));
|
|
197
195
|
});
|
|
198
196
|
}
|
|
199
197
|
return devicePromise;
|
|
200
198
|
}
|
|
201
199
|
|
|
200
|
+
/**
|
|
201
|
+
* 获取缓存的设备信息(网络状况,位置信息)
|
|
202
|
+
*/
|
|
203
|
+
function getCacheDeviceData(): IDeviceData {
|
|
204
|
+
return deviceData;
|
|
205
|
+
}
|
|
206
|
+
|
|
202
207
|
export default {
|
|
203
208
|
init,
|
|
204
209
|
getTms,
|
|
@@ -212,6 +217,6 @@ export default {
|
|
|
212
217
|
getAppScene,
|
|
213
218
|
canReport,
|
|
214
219
|
dataArrLen,
|
|
215
|
-
defaultDevice,
|
|
216
220
|
getDeviceData,
|
|
221
|
+
getCacheDeviceData,
|
|
217
222
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 负责组件的全埋点
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import helper from './helper';
|
|
6
|
+
|
|
7
|
+
let originalComponent: any = null;
|
|
8
|
+
|
|
9
|
+
// 劫持其他事件
|
|
10
|
+
function proxyHandle(componentName: string, methods: any, methodName: string): void {
|
|
11
|
+
const original = methods[methodName];
|
|
12
|
+
// eslint-disable-next-line no-param-reassign
|
|
13
|
+
methods[methodName] = function (...args: any[]): any {
|
|
14
|
+
// 执行原函数之后再发埋点
|
|
15
|
+
return helper.executeFunc(this, original, args, () => {
|
|
16
|
+
const data = helper.serializeData(this.data);
|
|
17
|
+
helper.reportData(`Component_${componentName}`, methodName, data);
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 劫持Page
|
|
23
|
+
function init(): void {
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
originalComponent = Component;
|
|
26
|
+
// @ts-ignore
|
|
27
|
+
Component = function (options: any) {
|
|
28
|
+
// 只有tms的页面才需要上报
|
|
29
|
+
if (options.tmsAutoReport) {
|
|
30
|
+
// tmsReportEvents是由工具在编译时分析出的要上报的事件列表
|
|
31
|
+
const methods = options.methods || {};
|
|
32
|
+
for (const name of options.tmsReportEvents) {
|
|
33
|
+
proxyHandle(options.tmsComponentName, methods, name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
originalComponent(options);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default {
|
|
41
|
+
init,
|
|
42
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动埋点辅助类
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
let reporter: any = null;
|
|
6
|
+
|
|
7
|
+
function reportData(...args: any[]): any {
|
|
8
|
+
if (reporter === null) {
|
|
9
|
+
// 如果是在app.js的onLaunch中调用,则没有getApp().tms为空
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
const tms = getApp()?.tms || wx.tms;
|
|
12
|
+
reporter = tms.getReporter();
|
|
13
|
+
}
|
|
14
|
+
// console.log('自动埋点:', ...args);
|
|
15
|
+
reporter.report2(...args);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 空函数
|
|
20
|
+
*/
|
|
21
|
+
function emptyFunc(): void {
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 执行函数
|
|
26
|
+
* @param context
|
|
27
|
+
* @param func
|
|
28
|
+
* @param args
|
|
29
|
+
* @param doneCallback
|
|
30
|
+
*/
|
|
31
|
+
function executeFunc(context: any, func: Function, args: any[], doneCallback: Function): any {
|
|
32
|
+
const value = func.apply(context, args);
|
|
33
|
+
// 如果不会返回promise
|
|
34
|
+
if (!value || !value.then || typeof value.then !== 'function') {
|
|
35
|
+
doneCallback();
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return value.then((res: any) => {
|
|
40
|
+
doneCallback();
|
|
41
|
+
return res;
|
|
42
|
+
}).catch((err: any) => {
|
|
43
|
+
doneCallback();
|
|
44
|
+
return Promise.reject(err);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const maxDepth = 5;
|
|
49
|
+
const maxArrLen = 10;
|
|
50
|
+
const maxStrLen = 30;
|
|
51
|
+
const maxFieldLen = 20;
|
|
52
|
+
|
|
53
|
+
function isBasicsType(obj: any): { isBasics: boolean, value: any } {
|
|
54
|
+
const type = typeof obj;
|
|
55
|
+
if (obj === null || obj === undefined
|
|
56
|
+
|| type === 'number' || type === 'boolean' || type === 'bigint') {
|
|
57
|
+
return { isBasics: true, value: obj };
|
|
58
|
+
}
|
|
59
|
+
if (type === 'string') {
|
|
60
|
+
const value = obj.substr(0, maxStrLen);
|
|
61
|
+
return { isBasics: true, value };
|
|
62
|
+
}
|
|
63
|
+
if (type === 'function' || type === 'symbol') {
|
|
64
|
+
return { isBasics: true, value: undefined };
|
|
65
|
+
}
|
|
66
|
+
return { isBasics: false, value: obj };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isArrayType(obj: any): { isArray: boolean, value: any } {
|
|
70
|
+
if (Array.isArray(obj)) {
|
|
71
|
+
const arr = obj.slice(0, maxArrLen);
|
|
72
|
+
return { isArray: true, value: arr };
|
|
73
|
+
}
|
|
74
|
+
return { isArray: false, value: obj };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 深表克隆
|
|
79
|
+
* @param obj
|
|
80
|
+
* @param depth 当前克隆的深度
|
|
81
|
+
*/
|
|
82
|
+
function deepClone(obj: any, depth: number): any {
|
|
83
|
+
if (depth >= maxDepth) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
const res1 = isBasicsType(obj);
|
|
87
|
+
if (res1.isBasics) {
|
|
88
|
+
return res1.value;
|
|
89
|
+
}
|
|
90
|
+
const res2 = isArrayType(obj);
|
|
91
|
+
if (res2.isArray) {
|
|
92
|
+
return res2.value.map((t: any) => deepClone(t, depth + 1));
|
|
93
|
+
}
|
|
94
|
+
const names = Object.getOwnPropertyNames(obj).slice(0, maxFieldLen);
|
|
95
|
+
const newObj = {};
|
|
96
|
+
for (const name of names) {
|
|
97
|
+
if (name === '__webviewId__') {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const value = obj[name];
|
|
101
|
+
const res1 = isBasicsType(value);
|
|
102
|
+
if (res1.isBasics) {
|
|
103
|
+
// @ts-ignore
|
|
104
|
+
newObj[name] = res1.value;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const res2 = isArrayType(value);
|
|
108
|
+
if (res2.isArray) {
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
newObj[name] = res2.value.map((t: any) => deepClone(t, depth + 1));
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
// @ts-ignore
|
|
114
|
+
newObj[name] = deepClone(value, depth + 1);
|
|
115
|
+
}
|
|
116
|
+
return newObj;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 序列化data
|
|
121
|
+
* @param data
|
|
122
|
+
*/
|
|
123
|
+
function serializeData(data: any): string {
|
|
124
|
+
const obj = deepClone(data, 0);
|
|
125
|
+
return JSON.stringify(obj);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default {
|
|
129
|
+
reportData,
|
|
130
|
+
emptyFunc,
|
|
131
|
+
executeFunc,
|
|
132
|
+
serializeData,
|
|
133
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 负责页面和组件的全埋点
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import proxyPage from './page';
|
|
6
|
+
import proxyComponent from './component';
|
|
7
|
+
|
|
8
|
+
let isInit = false;
|
|
9
|
+
|
|
10
|
+
function init(): void {
|
|
11
|
+
if (!isInit) {
|
|
12
|
+
isInit = true;
|
|
13
|
+
proxyPage.init();
|
|
14
|
+
proxyComponent.init();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
init,
|
|
20
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 负责页面的全埋点
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import helper from './helper';
|
|
6
|
+
|
|
7
|
+
// 必须上报的生命周期函数
|
|
8
|
+
const mustLifeMethods = ['onLoad', 'onShow', 'onReady', 'onHide', 'onUnload'];
|
|
9
|
+
// 可能会有的生命周期函数
|
|
10
|
+
const maybeLifeMethods = [
|
|
11
|
+
'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage',
|
|
12
|
+
'onShareTimeline', 'onAddToFavorites', 'onPageScroll',
|
|
13
|
+
];
|
|
14
|
+
const lifeMethods = mustLifeMethods.concat(maybeLifeMethods);
|
|
15
|
+
|
|
16
|
+
let originalPage: any = null;
|
|
17
|
+
|
|
18
|
+
// 劫持Page的生命周期
|
|
19
|
+
function proxyLifeMethod(pageOptions: any, methodName: string): void {
|
|
20
|
+
if (!pageOptions[methodName] && !mustLifeMethods.includes(methodName)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const original = pageOptions[methodName] || helper.emptyFunc;
|
|
24
|
+
// eslint-disable-next-line no-param-reassign
|
|
25
|
+
pageOptions[methodName] = function (...args: any[]): any {
|
|
26
|
+
let elapsed: number | '' = ''; // 页面曝光时间
|
|
27
|
+
let showCount: number | '' = ''; // 曝光次数
|
|
28
|
+
if (methodName === 'onShow') {
|
|
29
|
+
this.tmsPageShowCount = (this.tmsPageShowCount || 0) + 1;
|
|
30
|
+
this.tmsPageShowTime = Date.now();
|
|
31
|
+
showCount = this.tmsPageShowCount;
|
|
32
|
+
} else if (methodName === 'onHide' || methodName === 'onUnload') {
|
|
33
|
+
elapsed = Date.now() - this.tmsPageShowTime;
|
|
34
|
+
this.tmsPageShowTime = 0;
|
|
35
|
+
showCount = this.tmsPageShowCount;
|
|
36
|
+
}
|
|
37
|
+
// 执行原函数之后再发埋点
|
|
38
|
+
return helper.executeFunc(this, original, args, () => {
|
|
39
|
+
const data = helper.serializeData(this.data);
|
|
40
|
+
helper.reportData(`Page_${methodName}`, data, showCount, elapsed);
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 劫持其他事件
|
|
46
|
+
function proxyHandle(pageOptions: any, methodName: string): void {
|
|
47
|
+
const original = pageOptions[methodName] || helper.emptyFunc;
|
|
48
|
+
// eslint-disable-next-line no-param-reassign
|
|
49
|
+
pageOptions[methodName] = function (...args: any[]): any {
|
|
50
|
+
// 执行原函数之后再发埋点
|
|
51
|
+
return helper.executeFunc(this, original, args, () => {
|
|
52
|
+
const data = helper.serializeData(this.data);
|
|
53
|
+
helper.reportData(`Page_${methodName}`, data);
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 劫持Page
|
|
59
|
+
function init(): void {
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
originalPage = Page;
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
Page = function (options: any) {
|
|
64
|
+
// 只有tms的页面才需要上报
|
|
65
|
+
if (options.tmsAutoReport) {
|
|
66
|
+
for (const methodName of lifeMethods) {
|
|
67
|
+
proxyLifeMethod(options, methodName);
|
|
68
|
+
}
|
|
69
|
+
// tmsReportEvents是由工具在编译时分析出的要上报的事件列表
|
|
70
|
+
for (const methodName of options.tmsReportEvents) {
|
|
71
|
+
proxyHandle(options, methodName);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
originalPage(options);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default {
|
|
79
|
+
init,
|
|
80
|
+
};
|