vue项目前端通用埋点方案
后台-插件-广告管理-内容页头部广告(手机) |
埋点方案主要流程
1、 在 main.js 文件中生成 capol-log-uuid 埋点会话唯一id,并存入sessionStorage中
router.afterEach((to, from, next) => { //优先取url上面携带的埋点id const uuid = to.query.logId ? to.query.logId : Utils.uuid() console.log('进入页面,生成回话id') sessionStorage.setItem('capol-log-uuid',uuid) })- 1
- 2
- 3
- 4
- 5
- 6
2、在 utils 文件夹下添加 commonLog.js 公共埋点方法类,提供3个方法:
- 添加埋点函数:CapolLog.pointAdd(dynamicInfo, el)
- 更新埋点函数:CapolLog.pointUpdate(id, type,updateData)
- 更新埋点辅助函数:CapolLog.pointUpdateHelper(event,operateResultBool)
3、封装v-capol-log指令,监听元素点击事件,触发埋点
//通用按钮埋点指令 Vue.directive('capol-log', { inserted(el, binding) { //按钮点击执行事件 const handleClick = (el, binding) => { //修饰符 const idFlag = binding.modifiers.idFlag ? 1 : 0; //绑定值 const data = binding.value //埋点外部传参对象 const dynamicInfo = { idFlag, ...data } CapolLog.pointAdd(dynamicInfo,el) } const wrappedClickEvent = function (event) { handleClick(el, binding, event) } el.addEventListener('click', Utils.debounce(wrappedClickEvent, 300)) el.handleClick = handleClick }, unbind(el, binding) { const handleClick = el.handleClick el.removeEventListener('click', handleClick) delete el.handleClick }, })- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
4、将埋点公共方法添加到Vue.prototype原型对象中,手动调用,触发埋点
startTask(event) { //模拟接口响应时间2s setTimeout(() => { this.$CapolLog .pointUpdateHelper(event, true) .then((res) => { console.log(res) }) .catch((error) => { console.error(error) }) }, 2000) this.getMajorData() this.showStartTaskDialog = true },- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
埋点接口传参说明
添加埋点参数
字段 | 是否必填 | 备注说明 |
---|---|---|
source | 是 | 来源端 |
functionType | 是 | 功能类型 |
logType | 是 | 日志类型 |
info | 是 | 埋点基本信息 |
uuid | 是 | 回话唯一值 |
menuCode | 否 | 菜单编码 |
buttonName | 否(点击按钮为必填) | 按钮名称(应该带上下文) |
privateData | 是 | 私有参数json字符串 |
idFlag | 是 | id值是否需要返回,后续用于更新 |
更新埋点参数
字段 | 是否必填 | 备注说明 |
---|---|---|
functionType | 是 | 功能类型(和添加埋点时传参保持一致) |
updateData | 是 | 更新埋点参数json字符串(必须包含id字段,其他为更新参数) |
部分参数枚举说明
functionType(功能类型)
功能类型key | 功能类型val |
---|---|
0 | 按钮点击 |
1 | 页面初始 |
2 | 文件下载 |
3 | 文件浏览 |
4 | 文件分享 |
5 | 文件上传 |
6 | 选择模板 |
7 | 进度浏览 |
8 | 管控事件 |
9 | 图模关联 |
10 | 问题导出 |
logType(日志类型)
功能类型key | 功能类型val |
---|---|
0 | 工具埋点点击 |
1 | 框架埋点初始 |
2 | 文件项目文件下载 |
3 | 文件分享文件下载 |
4 | 管控文件下载 |
5 | 项目文件浏览 |
6 | 分享文件浏览 |
7 | 管控文件浏览 |
8 | 图纸文件浏览 |
9 | 项目文件分享 |
10 | 项目文件上传 |
11 | 管控文件上传 |
12 | 选择模板埋点 |
13 | 进度浏览埋点 |
14 | 进度管控埋点 |
15 | 图模关联埋点 |
16 | 问题导出埋点 |
source(来源端)
来源key | 来源Val |
---|---|
0 | pc |
1 | android |
2 | ios |
3 | wx |
4 | Capol3D |
5 | Capol2D |
指令埋点详细说明
实现方案
1、在需要埋点的元素上绑定 v-capol-log 指令,监听元素点击事件,当元素点击时,自动触发添加埋点方法,如果 v-capol-log 修饰符idFlag为true,在添加埋点成功后,在该点击元素上添加capol-log-element 类名、data-id(记录此次埋点id)、data-type(记录此次埋点类型)
<button type="button" class="el-button el-button--default el-button--small is-plain capol-log-element" data-id="1929582931271722" data-type="按钮点击">发起审批任务span>button>- 1
2、在点击事件相关业务执行完毕后,通过点击事件的event对象拿到元素上记录的埋点id、埋点类型type,将操作结果(‘“操作成功” || “操作失败”)作为更新参数作为updateData参数,调用CapolLog.pointUpdate(id, type,updateData)更新埋点方法实现埋点更新。后台将根据初次埋点时间、更新埋点时间,计算出****此次点击事件的响应时间responseTime。
使用方案
1、绑定指令
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
指令传参说明:
字段 | 是否必填 | 备注说明 |
---|---|---|
idFlag | 否 | 指令修饰符,后续需要更新埋点必传 |
functionType | 是 | 功能类型,根据枚举说明表传对应下标即可(eg:按钮点击传0) |
logType | 是 | 日志类型,根据枚举说明表传对应下标即可(eg:工具埋点点击传0) |
buttonName | 是 | 按钮名称,需要带上下文(eg:发起审批任务-成功审查) |
privateData | 是 | 私有参数对象,functionName(按钮名称)、menuName(菜单名称)这两个字段必传,其他字段按需传递即可 |
menuCode | 否 | 菜单编码,不传优先取左侧导航栏高亮菜单code,取不到取当前路由code |
2、点击事件业务执行完毕后,调用**this.$CapolLog.pointUpdateHelper(event,operateResultBool)**更新埋点(如果需要)
startTask(event) { //模拟接口响应时间2s setTimeout(() => { this.$CapolLog .pointUpdateHelper(event, true) .then((res) => { console.log(res) }) .catch((error) => { console.error(error) }) }, 2000) this.getMajorData() this.showStartTaskDialog = true },- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
页面初始埋点详细说明
待后续更新…
主要实现代码
commonLog.js(通用埋点方法)
import API from '../api' import router from '../router' import Utils from './utils' import store from '../store' import { getDeviceInfo } from './getDeviceInfo' // 获取功能类型 function getFunctionType(val) { const List = { 0: '按钮点击', 1: '页面初始', 2: '文件下载', 3: '文件浏览', 4: '文件分享', 5: '文件上传', 6: '选择模板', 7: '进度浏览', 8: '管控事件', 9: '图模关联', 10: '问题导出', } return List[val] || '' } // 获取日志类型 function getLogType(val) { const List = { 0: '工具埋点点击', 1: '框架埋点初始', 2: '项目文件下载', 3: '分享文件下载', 4: '管控文件下载', 5: '项目文件浏览', 6: '分享文件浏览', 7: '管控文件浏览', 8: '图纸文件浏览', 9: '项目文件分享', 10: '项目文件上传', 11: '管控文件上传', 12: '选择模板埋点', 13: '进度浏览埋点', 14: '进度管控埋点', 15: '图模关联埋点', 16: '问题导出埋点', } return List[val] || '' } // 获取来源端 // function getScource(val) { // const List = { // 0: 'pc', //WEB端 // 1: 'android', //安卓端 // 2: 'ios', //苹果端 // 3: 'wx', //小程序端 // 4: 'Capol3D', //速建端 // 5: 'Capol2D', //速绘端 // } // return List[val] || '' // } // 获取基本埋点信息 function getBasicInfo() { const basicInfo = getDeviceInfo(null, router.history.current) console.log(basicInfo, 'basicInfo') return basicInfo } //获取回话唯一id function getUUID() { let uuid = sessionStorage.getItem('capol-log-uuid') if (!uuid) { uuid = Utils.uuid() sessionStorage.setItem('capol-log-uuid', uuid) } return uuid } //按照后台要求 将对象转成json字符串 function getJSONObj(obj) { if (typeof obj === 'object' && obj !== null) { return JSON.stringify(obj).replace(/"/g, "'") } else { return '' } } // 扁平化系统菜单数组 function flatterArray(arr, finalArr = []) { if (arr.length === 0) return finalArr for (let i = 0; i < arr.length; i++) { const item = arr[i] if (item.children && item.children.length) { flatterArray(item.children, finalArr) } else { finalArr.push(item) } } return finalArr } //获取菜单code function getMenuCode() { //默认取路由名称 let code = router.currentRoute.name || '' let menuList = store.getters.menuList || [] if (menuList.length === 0) { const queryObj = Utils.getURLParameters(decodeURIComponent(window.location.href)) const { proId, enterpriseId } = queryObj menuList = JSON.parse(localStorage.getItem('pro_menu_list_' + proId + '_' + enterpriseId)) } //从系统菜单列表中取code if (menuList.length) { const flatterMenuList = flatterArray(menuList) const activeMenuText = document.querySelector( '.sidebar-el-menu .el-menu-item.is-active .item span' )?.innerHTML const activeMenu = flatterMenuList.find((el) => el.title === activeMenuText) if (activeMenu) { code = activeMenu.code } } return code } //埋点方法类 export default class CapolLog { /** * 通用添加埋点函数 * @param dynamicInfo 添加埋点的参数 (必填) * @param dynamicInfo 必填项说明:functionType(功能类型);logType(日志类型);privateData(埋点私有参数);idFlag(是否需要后台返回此次埋点id,用于后续更新) * @param el dom元素,如果有传,将往dom元素上添加埋点信息 (非必填) * @returns {Promise} 添加埋点的状态 */ static pointAdd = (dynamicInfo, el) => { let { functionType, source, logType, menuCode, buttonName, privateData, idFlag } = dynamicInfo const params = { source: source || 'pc', //来源端 functionType: getFunctionType(functionType), //功能类型 logType: getLogType(logType), //日志类型 info: getJSONObj(getBasicInfo()), //埋点基本信息 uuid: getUUID(), //回话唯一值 menuCode: menuCode || getMenuCode(), //菜单编码 (非必填) buttonName, //按钮名称 //私有参数json字符串 privateData: getJSONObj(privateData), idFlag: idFlag || 0, //id值是否需要返回 } return new Promise((resolve, reject) => { API.log .capolAddLog(params) .then((res) => { if (res.status === 200) { if (res.data && el) { el.classList.add('capol-log-element') el.setAttribute('data-id', res.data) el.setAttribute('data-type', getFunctionType(functionType)) } resolve(res) } else { reject(res) } }) .catch((error) => { reject(error) }) }) } /** * 更新埋点通用函数 * @param id 此次需要更新的埋点id (必填) * @param type 功能类型 eg('按钮点击') (必填) * @param updateData 更新的私有参数 (必填) * @returns {Promise} 返回更新埋点的状态 */ static pointUpdate = (id, type, updateData) => { const params = { functionType: type, updateData: getJSONObj({ id, ...updateData, }), } return new Promise((resolve, reject) => { API.log .capolUpdateLog(params) .then((res) => { if (res.status === 200) { resolve(res) } else { reject(res) } }) .catch((error) => { reject(error) }) }) } /** * 按钮点击更新埋点辅助函数 * @param event 点击事件的 event 对象 (必填) * @param operateResultBool 点击事件的操作结果 true || false (必填) * @returns {updatePromise} 返回更新埋点的状态 */ static pointUpdateHelper = async (event, operateResultBool) => { if (!event || operateResultBool === undefined) { return Promise.reject(new Error('更新埋点参数缺失')) } const updateData = { operateResult: operateResultBool ? '操作成功' : '操作失败', } console.log(event?.target.closest('.capol-log-element')) const targetBtn = event?.target.closest('.capol-log-element') //返回更新埋点的状态 let updatePromise if (targetBtn) { const id = targetBtn.dataset.id const type = targetBtn.dataset.type updatePromise = await this.pointUpdate(id, type, updateData) } else { updatePromise = Promise.reject(new Error('获取不到更新埋点id')) } return updatePromise } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。
在线投稿:投稿 站长QQ:1888636
后台-插件-广告管理-内容页尾部广告(手机) |