|
@@ -0,0 +1,377 @@
|
|
|
+/**
|
|
|
+ * 小程序埋点,接口使用神策SDK sensors
|
|
|
+ */
|
|
|
+
|
|
|
+import sensors from '@/lib/Sensorsdata-UniPlugin-JS/sensorsdata.es6.min.js';
|
|
|
+import store from '@/store';
|
|
|
+import {getGroupIdAndMallIdByLsbId} from '@/utils'
|
|
|
+
|
|
|
+/**
|
|
|
+ * 上传埋点数据至神策(第三方埋点平台)
|
|
|
+ * param eventName string 埋点事件名
|
|
|
+ * param properites object 埋点数据 (包含action, current event duration)
|
|
|
+ * param eventEnable boolean 是否允许上传埋点数据,默认为true
|
|
|
+ * param owner string 埋点负责人,默认为undefined
|
|
|
+ * */
|
|
|
+const normalTrack = (
|
|
|
+ eventName,
|
|
|
+ properites = {},
|
|
|
+ trackingEnable = true
|
|
|
+) => {
|
|
|
+ if (trackingEnable) {
|
|
|
+ sensors.track(eventName, properites);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 设置全埋点事件及埋点公共字段
|
|
|
+const trackingInit = (webViewParams) => {
|
|
|
+ // // 当前H5存在其他直接进入页面的形式,不会携带global对象,不需要初始化
|
|
|
+ // if (!webViewParams?.global) {
|
|
|
+ // return;
|
|
|
+ // }
|
|
|
+
|
|
|
+ const globalObj = webViewParams;
|
|
|
+ // 如果是非kerry+平台进入的方式,不进行埋点初始化
|
|
|
+ /*if (globalObj?.applicationType !== 'KERRY') {
|
|
|
+ return;
|
|
|
+ }*/
|
|
|
+
|
|
|
+ /* 设置项目名称 */
|
|
|
+ let project = 'kerryplus';
|
|
|
+ if (/PUDONG|JINGAN/.test(globalObj.source)) {
|
|
|
+ project = 'kerryon'
|
|
|
+ }
|
|
|
+ if (/CRM/.test(globalObj.source)) {
|
|
|
+ project = globalObj.source
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 设置lbsid */
|
|
|
+ let lbsId = globalObj?.lbsId || '';
|
|
|
+ if (!/CRM|PUDONG|JINGAN|ALIPAY/.test(globalObj.source)) {
|
|
|
+ const groupIdAndMallId = getGroupIdAndMallIdByLsbId(globalObj.lbsId);
|
|
|
+ groupId = groupIdAndMallId.groupId;
|
|
|
+ mallId = groupIdAndMallId.mallId;
|
|
|
+ lbsId = mallId
|
|
|
+ }
|
|
|
+
|
|
|
+ // eslint-disable-next-line no-console
|
|
|
+ console.log(
|
|
|
+ 'url=',
|
|
|
+ `${window.trackingBaseUrl}?project=${project}&remark=${window.trackingRemarkPre}_${lbsId}`
|
|
|
+ );
|
|
|
+
|
|
|
+ store.commit('SET_INIT_TRACKING', true);
|
|
|
+ store.commit('SET_INIT_TRACKING', true);
|
|
|
+
|
|
|
+
|
|
|
+ store.commit('SET_GLOBAL_CONFIG', globalObj);
|
|
|
+ store.commit('SET_THEME', globalObj);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //设置全埋点事件和其他参数
|
|
|
+ sensors.init({
|
|
|
+ server_url: `${window.trackingBaseUrl}?project=${project}&remark=${window.trackingRemarkPre}_${lbsId}`,
|
|
|
+ //数据接收地址
|
|
|
+ //其中lbs为用户当前的 环境_lbs。其中生产都报给t remark统一online_lbs
|
|
|
+ //qa和开发环境,统一报给 tq。其中发了qa的,remark使用qa_lbs作为区分。
|
|
|
+ //开发的remark用dev前缀+一个自定义的标签就行。这个主要是方便开发在后续开发自己测试埋点的时候,使用这个remark把自己埋的筛出来。
|
|
|
+ //项目名:嘉里中心小程序报crm,KerryON小程序报kerryon,Kerry+小程序报kerryplus,如果不是从webview拉起,而是浏览器进入H5,报h5service。
|
|
|
+
|
|
|
+ is_track_single_page: true, // 单页面配置,默认开启,若页面中有锚点设计,需要将该配置删除,否则触发锚点会多触发 $pageview 事件
|
|
|
+ use_client_time: true,
|
|
|
+ send_type: 'image', //这个地方与原版不同,没有采用beacon方式。因为不同的移动端对beacon的支持不一致
|
|
|
+ batch_send: {
|
|
|
+ datasend_timeout: 6000,
|
|
|
+ send_interval: 6000,
|
|
|
+ storage_length: 200,
|
|
|
+ },
|
|
|
+ heatmap: {
|
|
|
+ //是否开启点击图,default 表示开启,自动采集 $WebClick 事件,可以设置 'not_collect' 表示关闭。
|
|
|
+ clickmap: 'default',
|
|
|
+ //是否开启触达图,not_collect 表示关闭,不会自动采集 $WebStay 事件,可以设置 'default' 表示开启。
|
|
|
+ scroll_notice_map: 'default',
|
|
|
+ },
|
|
|
+ show_log: true,
|
|
|
+ source_channel: ['tpName', 'sa_utm'],
|
|
|
+ //子配置项 true 表示采集,false 表示不采集,未设置的参数取默认值。
|
|
|
+ preset_properties: {
|
|
|
+ //是否采集 $latest_utm 最近一次广告系列相关参数,默认值 true。
|
|
|
+ latest_utm: true,
|
|
|
+ //是否采集 $latest_traffic_source_type 最近一次流量来源类型,默认值 true。
|
|
|
+ latest_traffic_source_type: true,
|
|
|
+ //是否采集 $latest_search_keyword 最近一次搜索引擎关键字,默认值 true。
|
|
|
+ latest_search_keyword: true,
|
|
|
+ //是否采集 $latest_referrer 最近一次前向地址,默认值 true。
|
|
|
+ latest_referrer: true,
|
|
|
+ //是否采集 $latest_referrer_host 最近一次前向地址,1.14.8 以下版本默认是true,1.14.8 及以上版本默认是 false,需要手动设置为 true 开启。
|
|
|
+ latest_referrer_host: true,
|
|
|
+ //是否采集 $latest_landing_page 最近一次落地页地址,默认值 true。
|
|
|
+ latest_landing_page: true,
|
|
|
+ //是否采集 $url 页面地址作为公共属性,1.16.5 以下版本默认是 false,1.16.5 及以上版本默认是 true。
|
|
|
+ url: true,
|
|
|
+ //是否采集 $title 页面标题作为公共属性,1.16.5 以下版本默认是 false,1.16.5 及以上版本默认是 true。
|
|
|
+ title: true,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ //对于所有事件都需要添加的属性,可在初始化 SDK 前,调用 registerApp() 将属性注册为公共属性
|
|
|
+ if(project === 'kerryplus') {
|
|
|
+ sensors.registerPage({
|
|
|
+ saglobal_product_name: project,
|
|
|
+ saglobal_digital_brand_id: globalObj?.groupId,
|
|
|
+ saglobal_digital_brand_name: globalObj?.groupName,
|
|
|
+ saglobal_lbs_id: globalObj.mallId,
|
|
|
+ saglobal_lbs_name: globalObj.lbsName,
|
|
|
+ saglobal_open_id: globalObj.openId,
|
|
|
+ saglobal_union_id: globalObj.unionId,
|
|
|
+ saglobal_profile_id: globalObj.profileId,
|
|
|
+ saglobal_project: globalObj.projectId,
|
|
|
+ saglobal_building_unit: globalObj.building_unit,
|
|
|
+ saglobal_company: globalObj.company,
|
|
|
+ saglobal_position: globalObj.position,
|
|
|
+ saglobal_is_auth_mobile: globalObj.is_auth_mobile,
|
|
|
+ saglobal_is_auth_identity: globalObj.is_auth_identity,
|
|
|
+ });
|
|
|
+ const distinctID = globalObj.distinctId;
|
|
|
+ sensors.identify(distinctID, true);
|
|
|
+ } else {
|
|
|
+ sensors.registerPage({
|
|
|
+ saglobal_product_name: project,
|
|
|
+ saglobal_digital_brand_id: globalObj?.groupId,
|
|
|
+ saglobal_digital_brand_name: globalObj?.groupName,
|
|
|
+ saglobal_lbs_id: lbsId,
|
|
|
+ saglobal_lbs_name: globalObj.lbsName,
|
|
|
+ saglobal_open_id: globalObj.openId,
|
|
|
+ saglobal_union_id: globalObj.unionId,
|
|
|
+ saglobal_profile_id: globalObj.kipUserId,
|
|
|
+ saglobal_vipcode: globalObj.vipCode,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ // eslint-disable-next-line no-console
|
|
|
+ console.log('sensors', sensors);
|
|
|
+ // eslint-disable-next-line no-console
|
|
|
+ console.log('globalObj', globalObj);
|
|
|
+};
|
|
|
+
|
|
|
+sensors.quick('autoTrack'); //用于采集 $pageview 事件。
|
|
|
+
|
|
|
+class TrackFactory {
|
|
|
+ eventData = {};
|
|
|
+
|
|
|
+ isUpload = true;
|
|
|
+ eventName = '';
|
|
|
+ timeStart = new Date().getTime();
|
|
|
+ apiTimeStart = new Date().getTime();
|
|
|
+ static instanceRecorder = {};
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取埋点事件实例
|
|
|
+ * param eventName string 埋点事件名
|
|
|
+ * param eventData object 埋点数据
|
|
|
+ * param isForce boolean 是否强制实例化
|
|
|
+ * param isUpload boolean 是否允许上传埋点数据,默认为true
|
|
|
+ * param owner string 埋点负责人,默认为undefined
|
|
|
+ * param autoTrack1 boolean 初始化时自动上报一次 action 1,默认为 true
|
|
|
+ *
|
|
|
+ * return object
|
|
|
+ * */
|
|
|
+ static getInstance(
|
|
|
+ eventName = "",
|
|
|
+ eventData = '',
|
|
|
+ isForce = false,
|
|
|
+ isUpload,
|
|
|
+ owner,
|
|
|
+ autoTrack1
|
|
|
+ ) {
|
|
|
+ // 判断埋点事件名是否被注册,利用instanceRecorder记录被注册埋点事件实例
|
|
|
+ // 如果已经实例化过,就直接返回实例,无需再new
|
|
|
+ // 如果要强制实例化,可以把forceInit设为true
|
|
|
+ if (
|
|
|
+ !Object.prototype.hasOwnProperty.call(this.instanceRecorder, eventName) ||
|
|
|
+ isForce
|
|
|
+ ) {
|
|
|
+ eventData = eventData || {};
|
|
|
+ let autoTrack1_temp = true;
|
|
|
+ if (autoTrack1 === false) {
|
|
|
+ autoTrack1_temp = false;
|
|
|
+ }
|
|
|
+ this.instanceRecorder[eventName] = new TrackFactory(
|
|
|
+ eventName,
|
|
|
+ eventData,
|
|
|
+ isUpload,
|
|
|
+ owner,
|
|
|
+ autoTrack1_temp
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return this.instanceRecorder[eventName];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 销毁埋点事件实例
|
|
|
+ * param eventName string 埋点事件名
|
|
|
+ * */
|
|
|
+ static destory(eventName) {
|
|
|
+ delete this.instanceRecorder[eventName];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 埋点数据初始化
|
|
|
+ * param eventName string 埋点事件名
|
|
|
+ * param eventData object 埋点数据
|
|
|
+ * param isUpload boolean 是否允许上传埋点数据,默认为true
|
|
|
+ * param owner string 埋点负责人,默认为undefined
|
|
|
+ * param autoTrack1 boolean 初始化时自动上报一次 action 1,默认为 true
|
|
|
+ * */
|
|
|
+ constructor(
|
|
|
+ eventName = '',
|
|
|
+ eventData = '',
|
|
|
+ isUpload = true,
|
|
|
+ owner = "",
|
|
|
+ autoTrack1 = true
|
|
|
+ ) {
|
|
|
+ eventData = eventData || {};
|
|
|
+ eventData['owner'] = owner;
|
|
|
+ this.isUpload = isUpload;
|
|
|
+ this.eventName = eventName;
|
|
|
+ this.eventData = eventData;
|
|
|
+ if (autoTrack1) {
|
|
|
+ // 记录页面展示action
|
|
|
+ this.track(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录埋点的action
|
|
|
+ * param action number action code
|
|
|
+ * param onceData object 针对指定action的埋点数据,具体见埋点需求
|
|
|
+ * */
|
|
|
+ track = (action, onceData = {}) => {
|
|
|
+ if (action == 100 || action == 600) {
|
|
|
+ this.apiTimeStart = new Date().getTime();
|
|
|
+ this.eventData['user_mainstory_timecost'] =
|
|
|
+ new Date().getTime() - this.timeStart;
|
|
|
+ }
|
|
|
+
|
|
|
+ const trackData = { ...this.eventData, ...onceData };
|
|
|
+ trackData['action'] = action;
|
|
|
+ trackData['current_event_duration'] = new Date().getTime() - this.timeStart;
|
|
|
+ if (action == 200 || action == 400 || action == 700 || action == 800)
|
|
|
+ trackData['api_timecost'] = new Date().getTime() - this.apiTimeStart;
|
|
|
+ if (this.isUpload) {
|
|
|
+ normalTrack(this.eventName, trackData);
|
|
|
+ }
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 曝光监测选项
|
|
|
+ */
|
|
|
+// interface VisibilityObserverOption {
|
|
|
+// /** 要监听的节点的选择器,默认值 .watch-me */
|
|
|
+// selector: string;
|
|
|
+// /** 阈值,默认为 0.5 */
|
|
|
+// threshold: number;
|
|
|
+// /** 在 viewport 中停留多少秒才算曝光。默认为 1 */
|
|
|
+// delayInSecond: number;
|
|
|
+// /** 用来扩展(或收缩)参照节点布局区域的边界。默认值为 { bottom: 0 } */
|
|
|
+// margin: Partial<{
|
|
|
+// left: number;
|
|
|
+// right: number;
|
|
|
+// top: number;
|
|
|
+// bottom: number;
|
|
|
+// }>;
|
|
|
+// }
|
|
|
+
|
|
|
+/**
|
|
|
+ * 创建并返回一个 IntersectionObserver 对象实例。
|
|
|
+ * 该实例会观测 option.selector 节点。
|
|
|
+ * 当节点在 viewport 的观测区域停留 option.delayInSecond 秒,
|
|
|
+ * 就认为该节点已曝光,会触发 visibilityCb。
|
|
|
+ *
|
|
|
+ * 注意:
|
|
|
+ * 1. 节点多次曝光会触发多次 visibilityCb
|
|
|
+ * 2. 待观测的 option.selector 节点必须有唯一的 id
|
|
|
+ * 3. 调用该方法时,待观测的节点必须在已经在页面上渲染完成。一般在 onReady / mounted 方法中调用。
|
|
|
+ * 4. 在 onUnload / beforeDestroy 需要执行 observer.disconnect() 来释放资源。
|
|
|
+ * 5. observer 不能作为 vue 实例的响应式数据。
|
|
|
+ *
|
|
|
+ * https://uniapp.dcloud.net.cn/api/ui/intersection-observer.html#createintersectionobserver
|
|
|
+ *
|
|
|
+ * @param elContainer 自定义组件实例,一般传入 this
|
|
|
+ * @param visibilityCb 回调函数
|
|
|
+ * @param option
|
|
|
+ * @returns IntersectionObserver 对象实例。
|
|
|
+ */
|
|
|
+const getVisibilityObserver = (
|
|
|
+ elContainer,
|
|
|
+ visibilityCb,
|
|
|
+ option
|
|
|
+) => {
|
|
|
+ const opt = Object.assign(
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ selector: '.watch-me',
|
|
|
+ threshold: 0.5,
|
|
|
+ delayInSecond: 1,
|
|
|
+ margin: {
|
|
|
+ bottom: 0,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ option ? option : {}
|
|
|
+ );
|
|
|
+
|
|
|
+ let timer = null;
|
|
|
+ const inMap = {};
|
|
|
+ const outMap = {};
|
|
|
+ const delay = opt.delayInSecond * 1000;
|
|
|
+
|
|
|
+ const observer = uni.createIntersectionObserver(elContainer, {
|
|
|
+ observeAll: true,
|
|
|
+ thresholds: [opt.threshold],
|
|
|
+ initialRatio: opt.threshold,
|
|
|
+ });
|
|
|
+ observer.relativeToViewport(opt.margin).observe(opt.selector, (res) => {
|
|
|
+ // eslint-disable-next-line no-console
|
|
|
+ // console.log(res);
|
|
|
+
|
|
|
+ // res 身上除了 UniApp.ObserveResult 中列明的属性外
|
|
|
+ // 还有两个属性:
|
|
|
+ // - id:string,节点的 id
|
|
|
+ // - dataset:object,存放节点上面的 data-* 数据
|
|
|
+ // 所以我们可以用 id 来做 inMap 和 outMap 的 key。
|
|
|
+ const key = res.id;
|
|
|
+ // 进入 viewpoint
|
|
|
+ if (res.intersectionRatio > opt.threshold) {
|
|
|
+ inMap[key] = res;
|
|
|
+ if (outMap[key]) {
|
|
|
+ delete outMap[key];
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 离开 viewpoint
|
|
|
+ outMap[key] = res;
|
|
|
+ if (inMap[key]) {
|
|
|
+ // 计算停留时间
|
|
|
+ if (outMap[key].time - inMap[key].time >= delay) {
|
|
|
+ visibilityCb(key, outMap[key]);
|
|
|
+ delete outMap[key];
|
|
|
+ }
|
|
|
+ delete inMap[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理那些一直停留在 viewpoint 内的节点
|
|
|
+ if (timer) {
|
|
|
+ clearTimeout(timer);
|
|
|
+ }
|
|
|
+ timer = setTimeout(() => {
|
|
|
+ Object.keys(inMap).forEach((id) => {
|
|
|
+ visibilityCb(id, inMap[id]);
|
|
|
+ delete inMap[id];
|
|
|
+ });
|
|
|
+ }, delay);
|
|
|
+ });
|
|
|
+
|
|
|
+ return observer;
|
|
|
+};
|
|
|
+
|
|
|
+export { trackingInit, TrackFactory, getVisibilityObserver };
|