@uxda/appkit 4.2.93 → 4.2.95
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/PROJECT_DOCS.md +142 -0
- package/dist/index.js +1084 -23
- package/package.json +2 -1
- package/src/payment/api/endpoints.ts +1 -0
- package/src/payment/components/RechargeView.vue +2 -1
- package/src/payment/types.ts +1 -0
- package/src/shared/index.ts +1 -0
- package/src/shared/tracking/README.md +509 -0
- package/src/shared/tracking/directives/index.ts +40 -0
- package/src/shared/tracking/directives/track-click.ts +142 -0
- package/src/shared/tracking/directives/track-page.ts +50 -0
- package/src/shared/tracking/directives/track-scroll.ts +116 -0
- package/src/shared/tracking/directives/track-search.ts +179 -0
- package/src/shared/tracking/examples/data-structure-example.ts +239 -0
- package/src/shared/tracking/examples/directive-tracking-example.vue +202 -0
- package/src/shared/tracking/examples/global-tracking-example.vue +25 -0
- package/src/shared/tracking/examples/page-tracking-template.vue +27 -0
- package/src/shared/tracking/examples/sdk-tracking-example.vue +33 -0
- package/src/shared/tracking/index.ts +5 -0
- package/src/shared/tracking/tracking-init.ts +214 -0
- package/src/shared/tracking/tracking-sdk.ts +986 -0
- package/src/shared/tracking/useAutoTracking.ts +108 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 埋点点击指令
|
|
3
|
+
* 支持自定义点击内容埋点
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { App, Directive } from 'vue'
|
|
7
|
+
import { trackingSDK } from '../tracking-sdk'
|
|
8
|
+
|
|
9
|
+
// 指令值类型
|
|
10
|
+
interface TrackClickValue {
|
|
11
|
+
event?: string // 事件名称
|
|
12
|
+
data?: Record<string, unknown> // 自定义数据
|
|
13
|
+
elementId?: string // 元素ID
|
|
14
|
+
elementText?: string // 元素文本
|
|
15
|
+
preventDefault?: boolean // 是否阻止默认行为
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 获取元素信息
|
|
20
|
+
*/
|
|
21
|
+
function getElementInfo(el: HTMLElement): { id?: string; className?: string; text?: string } {
|
|
22
|
+
return {
|
|
23
|
+
id: el.id,
|
|
24
|
+
className: el.className,
|
|
25
|
+
text: (el.textContent || el.innerText || '').trim(),
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 获取点击位置
|
|
31
|
+
*/
|
|
32
|
+
function getClickPosition(event: Event): { x: number; y: number } {
|
|
33
|
+
const e = event as MouseEvent | TouchEvent
|
|
34
|
+
if ('touches' in e && e.touches.length > 0) {
|
|
35
|
+
return {
|
|
36
|
+
x: e.touches[0].clientX,
|
|
37
|
+
y: e.touches[0].clientY,
|
|
38
|
+
}
|
|
39
|
+
} else if ('clientX' in e) {
|
|
40
|
+
return {
|
|
41
|
+
x: e.clientX,
|
|
42
|
+
y: e.clientY,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { x: 0, y: 0 }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 绑定点击事件
|
|
50
|
+
*/
|
|
51
|
+
function bindClickEvent(el: HTMLElement, binding: any) {
|
|
52
|
+
// 先移除旧的事件监听器(必须使用相同的参数)
|
|
53
|
+
const oldHandler = (el as any).__trackClickHandler
|
|
54
|
+
const oldOptions = (el as any).__trackClickOptions
|
|
55
|
+
if (oldHandler) {
|
|
56
|
+
el.removeEventListener('click', oldHandler, oldOptions)
|
|
57
|
+
delete (el as any).__trackClickHandler
|
|
58
|
+
delete (el as any).__trackClickOptions
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const value = binding.value as TrackClickValue | string
|
|
62
|
+
|
|
63
|
+
// 处理字符串类型的值
|
|
64
|
+
const config: TrackClickValue = typeof value === 'string' ? { event: value } : value || {}
|
|
65
|
+
|
|
66
|
+
const { data = {}, elementId, elementText, preventDefault = false } = config
|
|
67
|
+
|
|
68
|
+
// 获取元素信息
|
|
69
|
+
const elementInfo = getElementInfo(el)
|
|
70
|
+
const finalElementId = elementId || elementInfo.id
|
|
71
|
+
const finalElementClass = elementInfo.className
|
|
72
|
+
const finalElementText = elementText || elementInfo.text
|
|
73
|
+
|
|
74
|
+
// 点击事件处理
|
|
75
|
+
const handleClick = (clickEvent: Event) => {
|
|
76
|
+
if (preventDefault) {
|
|
77
|
+
clickEvent.preventDefault()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 获取点击位置
|
|
81
|
+
const position = getClickPosition(clickEvent)
|
|
82
|
+
|
|
83
|
+
// 发送点击埋点
|
|
84
|
+
trackingSDK.trackClick(finalElementId || finalElementClass, finalElementText)
|
|
85
|
+
|
|
86
|
+
// 发送自定义事件埋点
|
|
87
|
+
if (config.event && config.event !== 'click') {
|
|
88
|
+
trackingSDK.trackCustom(config.event, {
|
|
89
|
+
elementId: finalElementId || finalElementClass,
|
|
90
|
+
elementText: finalElementText,
|
|
91
|
+
position,
|
|
92
|
+
...data,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 事件监听器选项
|
|
98
|
+
const eventOptions = { capture: true, passive: !preventDefault }
|
|
99
|
+
|
|
100
|
+
// 绑定事件(使用捕获阶段,确保能捕获到事件)
|
|
101
|
+
el.addEventListener('click', handleClick, eventOptions)
|
|
102
|
+
|
|
103
|
+
// 存储事件处理函数和选项,用于卸载时移除
|
|
104
|
+
;(el as any).__trackClickHandler = handleClick
|
|
105
|
+
;(el as any).__trackClickOptions = eventOptions
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 埋点点击指令实现
|
|
110
|
+
*/
|
|
111
|
+
const trackClickDirective: Directive = {
|
|
112
|
+
mounted(el: HTMLElement, binding) {
|
|
113
|
+
// 使用 nextTick 确保 DOM 完全渲染后再绑定
|
|
114
|
+
setTimeout(() => {
|
|
115
|
+
bindClickEvent(el, binding)
|
|
116
|
+
}, 0)
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
updated(el: HTMLElement, binding) {
|
|
120
|
+
bindClickEvent(el, binding)
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
unmounted(el: HTMLElement) {
|
|
124
|
+
// 移除事件监听器
|
|
125
|
+
const handler = (el as any).__trackClickHandler
|
|
126
|
+
const options = (el as any).__trackClickOptions
|
|
127
|
+
if (handler) {
|
|
128
|
+
el.removeEventListener('click', handler, options)
|
|
129
|
+
delete (el as any).__trackClickHandler
|
|
130
|
+
delete (el as any).__trackClickOptions
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 安装埋点点击指令
|
|
137
|
+
*/
|
|
138
|
+
export function installTrackClickDirective(app: App) {
|
|
139
|
+
app.directive('track-click', trackClickDirective)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export default trackClickDirective
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 埋点页面访问指令
|
|
3
|
+
* 支持页面访问埋点
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { App, Directive } from 'vue'
|
|
7
|
+
import { trackingSDK } from '../tracking-sdk'
|
|
8
|
+
|
|
9
|
+
// 指令值类型
|
|
10
|
+
interface TrackPageValue {
|
|
11
|
+
pageTitle?: string // 页面标题
|
|
12
|
+
customData?: Record<string, unknown> // 自定义数据
|
|
13
|
+
trackStay?: boolean // 是否跟踪停留时间
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 埋点页面访问指令实现
|
|
18
|
+
*/
|
|
19
|
+
const trackPageDirective: Directive = {
|
|
20
|
+
mounted(el: HTMLElement, binding) {
|
|
21
|
+
const value = binding.value as TrackPageValue | string
|
|
22
|
+
|
|
23
|
+
// 处理字符串类型的值
|
|
24
|
+
const config: TrackPageValue = typeof value === 'string' ? { pageTitle: value } : value || {}
|
|
25
|
+
|
|
26
|
+
const { pageTitle, customData = {}, trackStay = true } = config
|
|
27
|
+
|
|
28
|
+
// 记录页面访问
|
|
29
|
+
trackingSDK.trackPageView(pageTitle)
|
|
30
|
+
|
|
31
|
+
// 如果有自定义数据,发送自定义事件
|
|
32
|
+
if (Object.keys(customData).length > 0) {
|
|
33
|
+
trackingSDK.trackCustom('page_view_custom', customData)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 如果启用停留时间跟踪,记录开始时间
|
|
37
|
+
if (trackStay) {
|
|
38
|
+
;(el as any).__trackPageStartTime = Date.now()
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 安装埋点页面访问指令
|
|
45
|
+
*/
|
|
46
|
+
export function installTrackPageDirective(app: App) {
|
|
47
|
+
app.directive('track-page', trackPageDirective)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default trackPageDirective
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 埋点滚动指令
|
|
3
|
+
* 支持滚动事件埋点
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { App, Directive } from 'vue'
|
|
7
|
+
import { trackingSDK } from '../tracking-sdk'
|
|
8
|
+
|
|
9
|
+
// 指令值类型
|
|
10
|
+
interface TrackScrollValue {
|
|
11
|
+
threshold?: number // 滚动阈值(0-1)
|
|
12
|
+
data?: Record<string, unknown> // 自定义数据
|
|
13
|
+
trackDirection?: boolean // 是否跟踪滚动方向
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 埋点滚动指令实现
|
|
18
|
+
*/
|
|
19
|
+
const trackScrollDirective: Directive = {
|
|
20
|
+
mounted(el: HTMLElement, binding) {
|
|
21
|
+
const value = binding.value as TrackScrollValue | boolean
|
|
22
|
+
|
|
23
|
+
// 处理布尔值或配置对象
|
|
24
|
+
const config: TrackScrollValue =
|
|
25
|
+
typeof value === 'boolean' ? { threshold: 0.5, trackDirection: value } : value || {}
|
|
26
|
+
|
|
27
|
+
const { threshold = 0.5, data = {}, trackDirection = false } = config
|
|
28
|
+
|
|
29
|
+
let hasTracked = false
|
|
30
|
+
let lastScrollTop = 0
|
|
31
|
+
|
|
32
|
+
const handleScroll = () => {
|
|
33
|
+
if (hasTracked) return
|
|
34
|
+
|
|
35
|
+
const rect = el.getBoundingClientRect()
|
|
36
|
+
const windowHeight = window.innerHeight
|
|
37
|
+
const elementTop = rect.top
|
|
38
|
+
const elementHeight = rect.height
|
|
39
|
+
|
|
40
|
+
// 计算元素可见比例
|
|
41
|
+
const visibleHeight = Math.min(elementHeight, windowHeight - Math.max(0, elementTop))
|
|
42
|
+
const visibleRatio = visibleHeight / elementHeight
|
|
43
|
+
|
|
44
|
+
if (visibleRatio >= threshold) {
|
|
45
|
+
hasTracked = true
|
|
46
|
+
|
|
47
|
+
// 获取滚动方向
|
|
48
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop
|
|
49
|
+
const scrollDirection = scrollTop > lastScrollTop ? 'down' : 'up'
|
|
50
|
+
lastScrollTop = scrollTop
|
|
51
|
+
|
|
52
|
+
trackingSDK.trackCustom('scroll_into_view', {
|
|
53
|
+
elementId: el.id,
|
|
54
|
+
visibleRatio,
|
|
55
|
+
scrollDirection: trackDirection ? scrollDirection : undefined,
|
|
56
|
+
scrollTop,
|
|
57
|
+
...data,
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 使用 Intersection Observer 或 scroll 事件
|
|
63
|
+
if ('IntersectionObserver' in window) {
|
|
64
|
+
const observer = new IntersectionObserver(
|
|
65
|
+
(entries) => {
|
|
66
|
+
entries.forEach((entry) => {
|
|
67
|
+
if (entry.isIntersecting && entry.intersectionRatio >= threshold) {
|
|
68
|
+
hasTracked = true
|
|
69
|
+
trackingSDK.trackCustom('scroll_into_view', {
|
|
70
|
+
elementId: el.id,
|
|
71
|
+
intersectionRatio: entry.intersectionRatio,
|
|
72
|
+
...data,
|
|
73
|
+
})
|
|
74
|
+
observer.unobserve(el)
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
},
|
|
78
|
+
{ threshold }
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
observer.observe(el)
|
|
82
|
+
;(el as any).__scrollObserver = observer
|
|
83
|
+
} else {
|
|
84
|
+
// 降级到 scroll 事件
|
|
85
|
+
if (typeof window !== 'undefined') {
|
|
86
|
+
window.addEventListener('scroll', handleScroll, { passive: true })
|
|
87
|
+
;(el as any).__scrollHandler = handleScroll
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
unmounted(el: HTMLElement) {
|
|
93
|
+
// 清理 Intersection Observer
|
|
94
|
+
const observer = (el as any).__scrollObserver
|
|
95
|
+
if (observer) {
|
|
96
|
+
observer.disconnect()
|
|
97
|
+
delete (el as any).__scrollObserver
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 清理 scroll 事件
|
|
101
|
+
const handler = (el as any).__scrollHandler
|
|
102
|
+
if (handler && typeof window !== 'undefined') {
|
|
103
|
+
window.removeEventListener('scroll', handler)
|
|
104
|
+
delete (el as any).__scrollHandler
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 安装埋点滚动指令
|
|
111
|
+
*/
|
|
112
|
+
export function installTrackScrollDirective(app: App) {
|
|
113
|
+
app.directive('track-scroll', trackScrollDirective)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export default trackScrollDirective
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 埋点搜索指令
|
|
3
|
+
* 支持搜索行为埋点
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { App, Directive } from 'vue'
|
|
7
|
+
import { trackingSDK } from '../tracking-sdk'
|
|
8
|
+
import { debounce } from 'lodash-es'
|
|
9
|
+
|
|
10
|
+
// 指令值类型
|
|
11
|
+
interface TrackSearchValue {
|
|
12
|
+
event?: string // 事件名称
|
|
13
|
+
data?: Record<string, unknown> // 自定义数据
|
|
14
|
+
elementId?: string // 元素ID
|
|
15
|
+
trackInput?: boolean // 是否跟踪输入事件
|
|
16
|
+
trackSubmit?: boolean // 是否跟踪提交事件
|
|
17
|
+
debounceTime?: number // 防抖时间(毫秒)
|
|
18
|
+
minLength?: number // 最小搜索长度
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 获取搜索元素信息
|
|
23
|
+
*/
|
|
24
|
+
function getSearchElementInfo(el: HTMLElement): {
|
|
25
|
+
id?: string
|
|
26
|
+
className?: string
|
|
27
|
+
placeholder?: string
|
|
28
|
+
} {
|
|
29
|
+
let placeholder
|
|
30
|
+
if ((el as HTMLInputElement).placeholder) {
|
|
31
|
+
placeholder = (el as HTMLInputElement).placeholder
|
|
32
|
+
} else if (el.querySelector('input')) {
|
|
33
|
+
placeholder = (el.querySelector('input') as HTMLInputElement).placeholder
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
id: el.id,
|
|
38
|
+
className: el.className,
|
|
39
|
+
placeholder: placeholder || '',
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 埋点搜索指令实现
|
|
45
|
+
*/
|
|
46
|
+
const trackSearchDirective: Directive = {
|
|
47
|
+
mounted(el: HTMLElement, binding) {
|
|
48
|
+
const value = binding.value as TrackSearchValue | string
|
|
49
|
+
|
|
50
|
+
// 处理字符串类型的值
|
|
51
|
+
const config: TrackSearchValue = typeof value === 'string' ? { event: value } : value || {}
|
|
52
|
+
|
|
53
|
+
const {
|
|
54
|
+
event = 'search',
|
|
55
|
+
data = {},
|
|
56
|
+
elementId,
|
|
57
|
+
trackInput = true,
|
|
58
|
+
trackSubmit = true,
|
|
59
|
+
debounceTime = 500,
|
|
60
|
+
minLength = 1,
|
|
61
|
+
} = config
|
|
62
|
+
|
|
63
|
+
// 获取元素信息
|
|
64
|
+
const elementInfo = getSearchElementInfo(el)
|
|
65
|
+
const finalElementId = elementId || elementInfo.id
|
|
66
|
+
const finalElementClass = elementInfo.className
|
|
67
|
+
|
|
68
|
+
// 输入事件处理
|
|
69
|
+
const handleInput = (event: Event) => {
|
|
70
|
+
const target = event.target as HTMLInputElement
|
|
71
|
+
const searchValue = target.value.trim()
|
|
72
|
+
|
|
73
|
+
// 检查最小长度
|
|
74
|
+
if (searchValue.length < minLength) {
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 发送搜索输入埋点
|
|
79
|
+
trackingSDK.trackCustom('search_input', {
|
|
80
|
+
elementId: finalElementId || finalElementClass,
|
|
81
|
+
searchValue,
|
|
82
|
+
searchLength: searchValue.length,
|
|
83
|
+
placeholder: elementInfo.placeholder,
|
|
84
|
+
...data,
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 提交事件处理
|
|
89
|
+
const handleSubmit = (event: Event) => {
|
|
90
|
+
const target = event.target as HTMLInputElement
|
|
91
|
+
const searchValue = target.value.trim()
|
|
92
|
+
|
|
93
|
+
// 检查最小长度
|
|
94
|
+
if (searchValue.length < minLength) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 发送搜索提交埋点
|
|
99
|
+
trackingSDK.trackCustom('search_submit', {
|
|
100
|
+
elementId: finalElementId || finalElementClass,
|
|
101
|
+
searchValue,
|
|
102
|
+
searchLength: searchValue.length,
|
|
103
|
+
placeholder: elementInfo.placeholder,
|
|
104
|
+
...data,
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 创建防抖的输入处理函数
|
|
109
|
+
const debouncedHandleInput = debounce(handleInput, debounceTime || 500)
|
|
110
|
+
|
|
111
|
+
// 绑定事件
|
|
112
|
+
if (trackInput) {
|
|
113
|
+
el.addEventListener('input', debouncedHandleInput, { passive: true })
|
|
114
|
+
;(el as any).__trackSearchInputHandler = debouncedHandleInput
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (trackSubmit) {
|
|
118
|
+
el.addEventListener('keydown', (event: KeyboardEvent) => {
|
|
119
|
+
if (event.key === 'Enter') {
|
|
120
|
+
handleSubmit(event)
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
;(el as any).__trackSearchSubmitHandler = handleSubmit
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 如果是表单元素,也监听表单提交
|
|
127
|
+
const form = el.closest('form')
|
|
128
|
+
if (form && trackSubmit) {
|
|
129
|
+
form.addEventListener('submit', handleSubmit)
|
|
130
|
+
;(el as any).__trackSearchFormHandler = handleSubmit
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
updated(el: HTMLElement, binding) {
|
|
135
|
+
// 更新时重新绑定
|
|
136
|
+
const inputHandler = (el as any).__trackSearchInputHandler
|
|
137
|
+
|
|
138
|
+
if (inputHandler) {
|
|
139
|
+
el.removeEventListener('input', inputHandler)
|
|
140
|
+
delete (el as any).__trackSearchInputHandler
|
|
141
|
+
|
|
142
|
+
trackSearchDirective.mounted?.(el, binding)
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
unmounted(el: HTMLElement) {
|
|
147
|
+
// 移除事件监听器
|
|
148
|
+
const inputHandler = (el as any).__trackSearchInputHandler
|
|
149
|
+
const submitHandler = (el as any).__trackSearchSubmitHandler
|
|
150
|
+
const formHandler = (el as any).__trackSearchFormHandler
|
|
151
|
+
|
|
152
|
+
if (inputHandler) {
|
|
153
|
+
el.removeEventListener('input', inputHandler)
|
|
154
|
+
delete (el as any).__trackSearchInputHandler
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (submitHandler) {
|
|
158
|
+
el.removeEventListener('submit', submitHandler)
|
|
159
|
+
delete (el as any).__trackSearchSubmitHandler
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (formHandler) {
|
|
163
|
+
const form = el.closest('form')
|
|
164
|
+
if (form) {
|
|
165
|
+
form.removeEventListener('submit', formHandler)
|
|
166
|
+
}
|
|
167
|
+
delete (el as any).__trackSearchFormHandler
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 安装埋点搜索指令
|
|
174
|
+
*/
|
|
175
|
+
export function installTrackSearchDirective(app: App) {
|
|
176
|
+
app.directive('track-search', trackSearchDirective)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export default trackSearchDirective
|