您现在的位置是:首页 > 技术教程 正文

vue项目前端通用埋点方案

admin 阅读: 2024-03-30
后台-插件-广告管理-内容页头部广告(手机)

埋点方案主要流程

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个方法:

  1. 添加埋点函数:CapolLog.pointAdd(dynamicInfo, el)
  2. 更新埋点函数:CapolLog.pointUpdate(id, type,updateData)
  3. 更新埋点辅助函数: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字符串
idFlagid值是否需要返回,后续用于更新
更新埋点参数
字段是否必填备注说明
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
0pc
1android
2ios
3wx
4Capol3D
5Capol2D

指令埋点详细说明

实现方案

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

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索
排行榜