vue项目前端通用埋点方案

UpUp_seaboy 2024-07-03 08:33:02 阅读 75

埋点方案主要流程

1、 在 main.js 文件中生成 capol-log-uuid 埋点会话唯一id,并存入sessionStorage

<code>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)

})

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

},

})

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

},

埋点接口传参说明

添加埋点参数
字段 是否必填 备注说明
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(记录此次埋点类型)

<code><button type="button" class="el-button el-button--default el-button--small is-plain capol-log-element" data-id="1929582931271722" data-type="按钮点击">发起审批任务</span></button>code>

2、在点击事件相关业务执行完毕后,通过点击事件的event对象拿到元素上记录的埋点id、埋点类型type,将操作结果(‘“操作成功” || “操作失败”)作为更新参数作为updateData参数,调用CapolLog.pointUpdate(id, type,updateData)更新埋点方法实现埋点更新。后台将根据初次埋点时间、更新埋点时间,计算出****此次点击事件的响应时间responseTime

使用方案

1、绑定指令

<el-button

v-capol-log.idFlag="{

functionType: 0,

logType: 0,

buttonName: '发起审批任务-成功审查',

privateData: { functionName: '发起审批任务', menuName: '成果审查',projectId:123,enterpriseId:456 },

}"code>

plain

@click="startTask"code>

>发起审批任务

</el-button>

指令传参说明:

字段 是否必填 备注说明
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

},

页面初始埋点详细说明

待后续更新…

主要实现代码

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

}

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。