@rk911/tracker-sdk 1.6.4
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/core/compat.js +344 -0
- package/core/config.js +132 -0
- package/core/queue.js +461 -0
- package/core/session.js +128 -0
- package/core/tracker.js +287 -0
- package/customize/index.js +92 -0
- package/customize/modules/common.js +43 -0
- package/customize/modules/store-manager.js +56 -0
- package/index.js +403 -0
- package/package.json +21 -0
- package/platforms/app.js +36 -0
- package/platforms/weapp.js +218 -0
- package/platforms/web.js +483 -0
- package/plugins/api-watch.js +240 -0
- package/plugins/app-lifecycle.js +90 -0
- package/plugins/click.js +557 -0
- package/plugins/exposure.js +376 -0
- package/plugins/js-error.js +232 -0
- package/plugins/page-stay.js +44 -0
- package/plugins/page-view.js +172 -0
- package/plugins/performance.js +157 -0
- package/plugins/scroll-depth.js +256 -0
package/core/tracker.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { mergeConfig } from './config.js'
|
|
2
|
+
import { SessionManager } from './session.js'
|
|
3
|
+
import { ReportQueue } from './queue.js'
|
|
4
|
+
|
|
5
|
+
export class Tracker {
|
|
6
|
+
constructor(config, adapter) {
|
|
7
|
+
this.config = mergeConfig(config)
|
|
8
|
+
this.adapter = adapter
|
|
9
|
+
this.session = new SessionManager(adapter, this.config)
|
|
10
|
+
this.queue = new ReportQueue(adapter, this.config, this.session)
|
|
11
|
+
this.plugins = []
|
|
12
|
+
this._destroyed = false
|
|
13
|
+
this._userProperties = null
|
|
14
|
+
|
|
15
|
+
this._paramResolvers = {}
|
|
16
|
+
this._referrer = null
|
|
17
|
+
this._currentPage = null
|
|
18
|
+
this._currentScrollDepth = null
|
|
19
|
+
this._sampled = this._decideSampling()
|
|
20
|
+
this._validEventTypes = {
|
|
21
|
+
page_view: true, page_stay: true, click: true, custom: true,
|
|
22
|
+
api_request: true, js_error: true, app_lifecycle: true, page_show: true,
|
|
23
|
+
exposure: true, scroll_depth: true, performance: true,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_decideSampling() {
|
|
28
|
+
var sampling = this.config.sampling
|
|
29
|
+
if (sampling === undefined || sampling === null || sampling >= 1) {
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
if (sampling <= 0) return false
|
|
33
|
+
var sessionId = this.session.getSessionId()
|
|
34
|
+
var hash = 0
|
|
35
|
+
for (var i = 0; i < sessionId.length; i++) {
|
|
36
|
+
hash = ((hash << 5) - hash + sessionId.charCodeAt(i)) | 0
|
|
37
|
+
}
|
|
38
|
+
return Math.abs(hash % 100) / 100 < sampling
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_getDeviceFields() {
|
|
42
|
+
var info = this.adapter.getDeviceInfo() || {}
|
|
43
|
+
return {
|
|
44
|
+
deviceBrand: info.brand || null,
|
|
45
|
+
deviceModel: info.model || null,
|
|
46
|
+
os: info.platform || info.os || null,
|
|
47
|
+
osVersion: info.system || null,
|
|
48
|
+
screenWidth: info.screenWidth || null,
|
|
49
|
+
screenHeight: info.screenHeight || null,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---- 插件管理 ----
|
|
54
|
+
|
|
55
|
+
use(plugin) {
|
|
56
|
+
if (this._destroyed) return this
|
|
57
|
+
if (plugin.name && this.plugins.some(p => p.name === plugin.name)) {
|
|
58
|
+
if (this.config.debug) {
|
|
59
|
+
console.warn('[TrackerSDK] plugin "' + plugin.name + '" already registered, skipping')
|
|
60
|
+
}
|
|
61
|
+
return this
|
|
62
|
+
}
|
|
63
|
+
this.plugins.push(plugin)
|
|
64
|
+
plugin.install(this)
|
|
65
|
+
return this
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---- 自动采集上报 ----
|
|
69
|
+
|
|
70
|
+
track(eventType, data) {
|
|
71
|
+
if (this._destroyed) return
|
|
72
|
+
if (!this._sampled) return
|
|
73
|
+
|
|
74
|
+
if (!this._validEventTypes[eventType]) {
|
|
75
|
+
if (this.config.debug) {
|
|
76
|
+
console.warn('[TrackerSDK] invalid eventType "' + eventType + '", dropped')
|
|
77
|
+
}
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 先评估会话是否过期(空闲超 timeout 会在此滚动到新 session 并重置序列号),
|
|
82
|
+
// 必须在 touch() 之前,否则 touch 刷新 lastActivityTime 后过期判断永远为 false
|
|
83
|
+
this.session.getSessionId()
|
|
84
|
+
|
|
85
|
+
var seq = this.session.nextSequence()
|
|
86
|
+
this.session.touch()
|
|
87
|
+
|
|
88
|
+
var eventData = data || {}
|
|
89
|
+
|
|
90
|
+
// 只有导航事件(page_view)才更新页面链路状态(_currentPage/_referrer)
|
|
91
|
+
// page_stay/click 等事件传的 pageRoute 仅表示"事件发生在哪个页面",不是导航信号
|
|
92
|
+
var declaredPage = eventData.pageRoute || null
|
|
93
|
+
|
|
94
|
+
if (eventType === 'page_view' && declaredPage && declaredPage !== '/' && declaredPage !== this._currentPage) {
|
|
95
|
+
if (this._currentPage) {
|
|
96
|
+
this._referrer = this._currentPage
|
|
97
|
+
}
|
|
98
|
+
this._currentPage = declaredPage
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!this._currentPage) {
|
|
102
|
+
var initRoute = this.adapter.getCurrentPagePath()
|
|
103
|
+
if (initRoute && initRoute !== '/') {
|
|
104
|
+
this._currentPage = initRoute
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 动态参数解析:click 插件传入 _paramName 和 _element
|
|
109
|
+
if (eventData._paramName && this._paramResolvers[eventData._paramName]) {
|
|
110
|
+
try {
|
|
111
|
+
var resolved = this._paramResolvers[eventData._paramName](eventData._element)
|
|
112
|
+
if (resolved && typeof resolved === 'object') {
|
|
113
|
+
if (resolved.extKey) eventData.extKey = resolved.extKey
|
|
114
|
+
if (resolved.extValue) eventData.extValue = resolved.extValue
|
|
115
|
+
if (resolved.extIntValue !== undefined) eventData.extIntValue = resolved.extIntValue
|
|
116
|
+
if (resolved.postParam) eventData.postParam = resolved.postParam
|
|
117
|
+
}
|
|
118
|
+
} catch (e) {
|
|
119
|
+
if (this.config.debug) {
|
|
120
|
+
console.warn('[TrackerSDK] paramResolver error:', eventData._paramName, e)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
delete eventData._paramName
|
|
125
|
+
delete eventData._element
|
|
126
|
+
|
|
127
|
+
var event = {
|
|
128
|
+
eventId: this.adapter.generateId('e'),
|
|
129
|
+
eventType: eventType,
|
|
130
|
+
eventName: eventData.eventName || eventType,
|
|
131
|
+
timestamp: this.adapter.getTimestamp(),
|
|
132
|
+
sequence: seq,
|
|
133
|
+
pageRoute: 'pageRoute' in eventData ? (eventData.pageRoute || null) : (this._currentPage || this.adapter.getCurrentPagePath()),
|
|
134
|
+
pageTitle: 'pageTitle' in eventData ? (eventData.pageTitle || null) : (this.adapter.getPageTitle ? this.adapter.getPageTitle() : null),
|
|
135
|
+
elementId: eventData.elementId || null,
|
|
136
|
+
elementText: eventData.elementText || null,
|
|
137
|
+
duration: eventData.duration || null,
|
|
138
|
+
scrollDepth: eventData.scrollDepth || this._currentScrollDepth || null,
|
|
139
|
+
errorMessage: eventData.errorMessage || null,
|
|
140
|
+
errorStack: eventData.errorStack || null,
|
|
141
|
+
extraData: eventData.extraData ? JSON.stringify(eventData.extraData) : null,
|
|
142
|
+
extKey: eventData.extKey || null,
|
|
143
|
+
extValue: eventData.extValue || null,
|
|
144
|
+
extIntValue: eventData.extIntValue || null,
|
|
145
|
+
frontUrl: eventData.frontUrl || null,
|
|
146
|
+
postParam: eventData.postParam != null && eventData.postParam !== ''
|
|
147
|
+
? (typeof eventData.postParam === 'string'
|
|
148
|
+
? eventData.postParam
|
|
149
|
+
: JSON.stringify(eventData.postParam))
|
|
150
|
+
: '',
|
|
151
|
+
networkType: this.adapter.getNetworkType
|
|
152
|
+
? this.adapter.getNetworkType()
|
|
153
|
+
: null,
|
|
154
|
+
referrer: eventData.referrer || this._referrer || null,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 自引用 referrer(来源页 = 当前页)对路径还原无意义,清空。
|
|
158
|
+
// 常见于后台切回后 _referrer 未随导航更新,page_stay 继承到当前页自身。
|
|
159
|
+
if (event.referrer && event.referrer === event.pageRoute) {
|
|
160
|
+
event.referrer = null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
var deviceFields = this._getDeviceFields()
|
|
164
|
+
Object.keys(deviceFields).forEach(function (key) {
|
|
165
|
+
event[key] = deviceFields[key]
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
this.queue.push(event)
|
|
169
|
+
|
|
170
|
+
if (this.config.debug) {
|
|
171
|
+
console.log('[TrackerSDK] track:', eventType, event)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ---- 手动上报(走旧接口)----
|
|
176
|
+
|
|
177
|
+
trackLegacy(frontUrl, postParam, extraHeader) {
|
|
178
|
+
if (this._destroyed) return
|
|
179
|
+
|
|
180
|
+
this.session.touch()
|
|
181
|
+
|
|
182
|
+
var url = this.config.dadianUrl + '/api/point/task/activeReport'
|
|
183
|
+
var header = {
|
|
184
|
+
'content-type': 'application/json',
|
|
185
|
+
token: this.adapter.getToken() || '',
|
|
186
|
+
}
|
|
187
|
+
if (extraHeader) {
|
|
188
|
+
Object.keys(extraHeader).forEach(function (k) {
|
|
189
|
+
header[k] = extraHeader[k]
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
var data = {
|
|
193
|
+
frontUrl: frontUrl,
|
|
194
|
+
postParam:
|
|
195
|
+
typeof postParam === 'string' ? postParam : JSON.stringify(postParam),
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.queue.pushLegacy(url, data, header)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ---- 用户标识 ----
|
|
202
|
+
|
|
203
|
+
setUser(userId) {
|
|
204
|
+
this.session.setUser(userId)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
setDeviceId(deviceId) {
|
|
208
|
+
this.session.setDeviceId(deviceId)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
setUserProperties(properties) {
|
|
212
|
+
this._userProperties = properties
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
getUserProperties() {
|
|
216
|
+
return this._userProperties
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ---- 页面导航状态(referrer)----
|
|
220
|
+
|
|
221
|
+
updatePageRoute(newPage) {
|
|
222
|
+
if (newPage && newPage !== this._currentPage) {
|
|
223
|
+
if (this._currentPage) {
|
|
224
|
+
this._referrer = this._currentPage
|
|
225
|
+
}
|
|
226
|
+
this._currentPage = newPage
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
getReferrer() {
|
|
231
|
+
return this._referrer
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ---- 动态参数解析器 ----
|
|
235
|
+
|
|
236
|
+
registerParam(name, fn) {
|
|
237
|
+
if (typeof name === 'string' && typeof fn === 'function') {
|
|
238
|
+
this._paramResolvers[name] = fn
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
unregisterParam(name) {
|
|
243
|
+
delete this._paramResolvers[name]
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ---- 访问器 ----
|
|
247
|
+
|
|
248
|
+
getAdapter() {
|
|
249
|
+
return this.adapter
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
getSession() {
|
|
253
|
+
return this.session
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
getQueue() {
|
|
257
|
+
return this.queue
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
getConfig() {
|
|
261
|
+
return this.config
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ---- Hook ----
|
|
265
|
+
|
|
266
|
+
onReport(fn) {
|
|
267
|
+
this.queue.onReport(fn)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ---- 清理 ----
|
|
271
|
+
|
|
272
|
+
destroy() {
|
|
273
|
+
if (this._destroyed) return
|
|
274
|
+
this._destroyed = true
|
|
275
|
+
|
|
276
|
+
this.plugins.forEach(function (plugin) {
|
|
277
|
+
if (typeof plugin.destroy === 'function') {
|
|
278
|
+
plugin.destroy()
|
|
279
|
+
}
|
|
280
|
+
})
|
|
281
|
+
this.plugins = []
|
|
282
|
+
|
|
283
|
+
this.queue.destroy()
|
|
284
|
+
this.session.destroy()
|
|
285
|
+
this.adapter.destroy()
|
|
286
|
+
}
|
|
287
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK 定制化调度入口
|
|
3
|
+
*
|
|
4
|
+
* 目录结构:
|
|
5
|
+
* customize/
|
|
6
|
+
* index.js — 当前文件,统一调度入口
|
|
7
|
+
* modules/
|
|
8
|
+
* common.js — 技术栈通用方法(uni-app 所有项目共享)
|
|
9
|
+
* store-manager.js — 店长端项目定制(appId=1)
|
|
10
|
+
* (后续项目在此添加,如 customer-app.js、mini-program.js)
|
|
11
|
+
*
|
|
12
|
+
* 两种调用方式:
|
|
13
|
+
* 1. SDK 内部:config.projectRules.isTabbarClick(el)
|
|
14
|
+
* 2. 业务代码:customize.callFunction('store-manager', 'extractNavTarget', eventName)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// appId → 模块文件名映射
|
|
18
|
+
var APP_ID_MAP = {
|
|
19
|
+
'1': 'store-manager',
|
|
20
|
+
// '12': 'customer-app',
|
|
21
|
+
// '18': 'mini-program',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 动态加载 modules 下的模块
|
|
26
|
+
*/
|
|
27
|
+
function loadModule(moduleName) {
|
|
28
|
+
try {
|
|
29
|
+
var m = require('./modules/' + moduleName)
|
|
30
|
+
return m.default || m
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.warn('[customize] module not found:', moduleName)
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 调用指定模块的指定方法
|
|
39
|
+
* @param {string} moduleName - 模块名(common / store-manager / ...)
|
|
40
|
+
* @param {string} methodName - 方法名
|
|
41
|
+
* @param {...any} args - 参数(直接传给方法)
|
|
42
|
+
* @returns {any} 方法返回值
|
|
43
|
+
*/
|
|
44
|
+
function callFunction(moduleName, methodName) {
|
|
45
|
+
var mod = loadModule(moduleName)
|
|
46
|
+
if (!mod) return undefined
|
|
47
|
+
if (!methodName || typeof mod[methodName] !== 'function') {
|
|
48
|
+
console.warn('[customize] method not found:', moduleName + '.' + methodName)
|
|
49
|
+
return undefined
|
|
50
|
+
}
|
|
51
|
+
var args = Array.prototype.slice.call(arguments, 2)
|
|
52
|
+
return mod[methodName].apply(null, args)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 根据 appId 获取合并后的项目规则(common + 项目模块)
|
|
57
|
+
* SDK 初始化时调用,返回值赋给 config.projectRules
|
|
58
|
+
*
|
|
59
|
+
* @param {string|number} appId
|
|
60
|
+
* @returns {object|null} 合并后的 rules 对象
|
|
61
|
+
*/
|
|
62
|
+
function getProjectRules(appId) {
|
|
63
|
+
if (!appId) return null
|
|
64
|
+
var projectName = APP_ID_MAP[String(appId)]
|
|
65
|
+
if (!projectName) return null
|
|
66
|
+
|
|
67
|
+
var commonMod = loadModule('common') || {}
|
|
68
|
+
var projectMod = loadModule(projectName) || {}
|
|
69
|
+
|
|
70
|
+
// 项目模块优先,common 兜底
|
|
71
|
+
var rules = {}
|
|
72
|
+
var key
|
|
73
|
+
for (key in commonMod) {
|
|
74
|
+
if (commonMod.hasOwnProperty(key)) {
|
|
75
|
+
rules[key] = commonMod[key]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (key in projectMod) {
|
|
79
|
+
if (projectMod.hasOwnProperty(key)) {
|
|
80
|
+
rules[key] = projectMod[key]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return rules
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { callFunction, getProjectRules }
|
|
88
|
+
|
|
89
|
+
export default {
|
|
90
|
+
callFunction: callFunction,
|
|
91
|
+
getProjectRules: getProjectRules,
|
|
92
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 技术栈通用定制方法(uni-app 全项目共享)
|
|
3
|
+
*
|
|
4
|
+
* 这里放的是所有 uni-app 项目都需要的通用检测/处理逻辑,
|
|
5
|
+
* 与具体业务项目无关。项目级别的特殊处理放各自的模块文件里。
|
|
6
|
+
*/
|
|
7
|
+
export default {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 检测元素是否在原生 uni-tabbar 内
|
|
11
|
+
* uni-app 内置 tabbar 会渲染为 <uni-tabbar> 标签或带 uni-tabbar class 的元素
|
|
12
|
+
* 所有 uni-app 项目通用,不需要项目定制
|
|
13
|
+
*/
|
|
14
|
+
isNativeTabbar: function (_this, el) {
|
|
15
|
+
var current = el
|
|
16
|
+
var depth = 0
|
|
17
|
+
while (current && current !== document.body && depth < 10) {
|
|
18
|
+
var tag = (current.tagName || '').toLowerCase()
|
|
19
|
+
if (tag === 'uni-tabbar' || tag === 'tabbar') return true
|
|
20
|
+
var cls = current.className || ''
|
|
21
|
+
if (typeof cls === 'string') {
|
|
22
|
+
if (cls.indexOf('uni-tabbar') > -1 || cls.indexOf('tabBar') > -1 || cls.indexOf('tab-bar') > -1) return true
|
|
23
|
+
}
|
|
24
|
+
current = current.parentElement || current.parentNode
|
|
25
|
+
depth++
|
|
26
|
+
}
|
|
27
|
+
return false
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 获取当前页面路由(uni-app 通用)
|
|
32
|
+
* 通过 getCurrentPages() 获取,所有 uni-app 项目一致
|
|
33
|
+
*/
|
|
34
|
+
getCurrentRoute: function () {
|
|
35
|
+
try {
|
|
36
|
+
var pages = getCurrentPages()
|
|
37
|
+
if (pages.length > 0) {
|
|
38
|
+
return pages[pages.length - 1].route || ''
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {}
|
|
41
|
+
return ''
|
|
42
|
+
},
|
|
43
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 店长端 (store-manager) 项目定制方法
|
|
3
|
+
* appId: 1
|
|
4
|
+
*
|
|
5
|
+
* 只放和店长端项目业务相关的特殊处理,
|
|
6
|
+
* 技术栈通用的逻辑在 common.js 里。
|
|
7
|
+
*/
|
|
8
|
+
export default {
|
|
9
|
+
|
|
10
|
+
name: 'store-manager',
|
|
11
|
+
appId: '1',
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 判断点击是否在店长端自定义底部导航区域
|
|
15
|
+
* 店长端用 footer-h.vue 组件,class="footers" / "footer-item"
|
|
16
|
+
* 返回 true 时 click.js 跳过自动采集(由业务代码手动 $track 上报)
|
|
17
|
+
*/
|
|
18
|
+
isTabbarClick: function (_this, el) {
|
|
19
|
+
var current = el
|
|
20
|
+
var depth = 0
|
|
21
|
+
while (current && current !== document.body && depth < 10) {
|
|
22
|
+
var cls = current.className || ''
|
|
23
|
+
if (typeof cls === 'string') {
|
|
24
|
+
if (cls.indexOf('footers') > -1 || cls.indexOf('footer-item') > -1) return true
|
|
25
|
+
}
|
|
26
|
+
current = current.parentElement || current.parentNode
|
|
27
|
+
depth++
|
|
28
|
+
}
|
|
29
|
+
return false
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 从导航事件中提取目标页面路由
|
|
34
|
+
* 店长端 footer-h 组件的 $track 调用格式:this.$track('footer/click' + url)
|
|
35
|
+
* 其中 url 为 /pages/xxx/index
|
|
36
|
+
* 返回目标路由供 SDK 提前更新 referrer 链路
|
|
37
|
+
*/
|
|
38
|
+
extractNavTarget: function (_this, eventName) {
|
|
39
|
+
if (!eventName) return null
|
|
40
|
+
var match = eventName.match(/footer\/click(\/pages\/[^\s?#]+)/)
|
|
41
|
+
if (match) return match[1]
|
|
42
|
+
return null
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 不需要自动采集 click 的选择器/区域
|
|
47
|
+
* 这些区域已有手动埋点,自动采集会导致重复或错误 pageRoute
|
|
48
|
+
*/
|
|
49
|
+
ignoreClickSelectors: ['.footers', '.footer-item'],
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 旧接口上报用的 frontUrl(各项目已在后台注册的值)
|
|
53
|
+
*/
|
|
54
|
+
pageStayFrontUrl: '/storeManager/page_stay',
|
|
55
|
+
pageShowFrontUrl: '/storeManager/page_show',
|
|
56
|
+
}
|