提交 04edfa15 编写于 作者: Q qiang

Merge branch 'dev' into alpha

'use strict';
/**
* 获取系统信息
*/
const sys = uni.getSystemInfoSync();
// 访问开始即启动小程序,访问结束结分为:进入后台超过5min、在前台无任何操作超过30min、在新的来源打开小程序;
const STAT_VERSION = '0.0.1';
const STAT_URL = 'https://tongji.dcloud.io/uni/stat';
const STAT_H5_URL = 'https://tongji.dcloud.io/uni/stat.gif';
const PAGE_PVER_TIME = 1800; // 页面在前台无操作结束访问时间 单位s
const APP_PVER_TIME = 300; // 应用在后台结束访问时间 单位s
const OPERATING_TIME = 10; // 数据上报时间 单位s
const DIFF_TIME = 60 * 1000 * 60 * 24;
let statConfig = {
appid: ''
};
let titleJsons = {};
// #ifdef VUE3
statConfig.appid = process.env.UNI_APP_ID;
titleJsons = process.env.UNI_STAT_TITLE_JSON;
// #endif
// #ifndef VUE3
statConfig = require('uni-stat-config').default || require('uni-stat-config');
const pagesTitle = require('uni-pages?{"type":"style"}').default;
let pagesData = pagesTitle.pages;
for (let i in pagesData) {
const style = pagesData[i];
const titleText =
// MP
style.navigationBarTitleText ||
// ali
style.defaultTitle||
// H5 || App
style.navigationBar?.titleText ||
'';
if (titleText) {
titleJsons[i] = titleText;
}
}
// #endif
const UUID_KEY = '__DC_STAT_UUID';
const UUID_VALUE = '__DC_UUID_VALUE';
function getUuid() {
let uuid = '';
if (get_platform_name() === 'n') {
try {
uuid = plus.runtime.getDCloudId();
} catch (e) {
uuid = '';
}
return uuid
}
try {
uuid = uni.getStorageSync(UUID_KEY);
} catch (e) {
uuid = UUID_VALUE;
}
if (!uuid) {
uuid = Date.now() + '' + Math.floor(Math.random() * 1e7);
try {
uni.setStorageSync(UUID_KEY, uuid);
} catch (e) {
uni.setStorageSync(UUID_KEY, UUID_VALUE);
}
}
return uuid
}
/**
* 获取配置信息 如 appid
*/
const stat_config = statConfig;
const get_uuid = (statData) => {
// 有可能不存在 deviceId(一般不存在就是出bug了),就自己生成一个
return sys.deviceId || getUuid()
};
const get_sgin = (statData) => {
let arr = Object.keys(statData);
let sortArr = arr.sort();
let sgin = {};
let sginStr = '';
for (var i in sortArr) {
sgin[sortArr[i]] = statData[sortArr[i]];
sginStr += sortArr[i] + '=' + statData[sortArr[i]] + '&';
}
return {
sign: '',
options: sginStr.substr(0, sginStr.length - 1),
}
};
const get_encodeURIComponent_options = (statData) => {
let data = {};
for (let prop in statData) {
data[prop] = encodeURIComponent(statData[prop]);
}
return data
};
/**
* 获取当前平台
* 移动端 : 'n',
* h5 : 'h5',
* 微信 : 'wx',
* 阿里 : 'ali',
* 百度 : 'bd',
* 头条 : 'tt',
* qq : 'qq',
* 快应用 : 'qn',
* 快手 : 'ks',
* 飞书 : 'lark',
* 快应用 : 'qw',
* 钉钉 : 'dt'
*/
const get_platform_name = () => {
// 苹果审核代码中禁止出现 alipay 字样 ,需要特殊处理一下
const aliArr = ['y', 'a', 'p', 'mp-ali'];
const platformList = {
'app': 'n',
'app-plus': 'n',
h5: 'h5',
'mp-weixin': 'wx',
[aliArr.reverse().join('')]: 'ali',
'mp-baidu': 'bd',
'mp-toutiao': 'tt',
'mp-qq': 'qq',
'quickapp-native': 'qn',
'mp-kuaishou': 'ks',
'mp-lark': 'lark',
'quickapp-webview':'qw'
};
if(platformList[process.env.VUE_APP_PLATFORM] === 'ali'){
if(my&&my.env){
const clientName = my.env.clientName;
if(clientName === 'ap') return 'ali'
if(clientName === 'dingtalk') return 'dt'
// TODO 缺少 ali 下的其他平台
}
}
return platformList[process.env.VUE_APP_PLATFORM]
};
/**
* 获取小程序 appid
*/
const get_pack_name = () => {
let packName = '';
if (get_platform_name() === 'wx' || get_platform_name() === 'qq') {
// 兼容微信小程序低版本基础库
if (uni.canIUse('getAccountInfoSync')) {
packName = uni.getAccountInfoSync().miniProgram.appId || '';
}
}
if (get_platform_name() === 'n') ;
return packName
};
/**
* 应用版本
*/
const get_version = () => {
return get_platform_name() === 'n' ? plus.runtime.version : ''
};
/**
* 获取渠道
*/
const get_channel = () => {
const platformName = get_platform_name();
let channel = '';
if (platformName === 'n') {
channel = plus.runtime.channel;
}
return channel
};
/**
* 获取小程序场景值
* @param {Object} options 页面信息
*/
const get_scene = (options) => {
const platformName = get_platform_name();
let scene = '';
if (options) {
return options
}
if (platformName === 'wx') {
scene = uni.getLaunchOptionsSync().scene;
}
return scene
};
/**
* 获取拼接参数
*/
const get_splicing = (data) => {
let str = '';
for (var i in data) {
str += i + '=' + data[i] + '&';
}
return str.substr(0, str.length - 1)
};
/**
* 获取页面url,不包含参数
*/
const get_route = (pageVm) => {
let _self = pageVm || get_page_vm();
if (get_platform_name() === 'bd') {
let mp_route = _self.$mp && _self.$mp.page && _self.$mp.page.is;
let scope_route = _self.$scope && _self.$scope.is;
return mp_route || scope_route || ''
} else {
return _self.route || (_self.$scope && _self.$scope.route) || (_self.$mp && _self.$mp.page.route)
}
};
/**
* 获取页面url, 包含参数
*/
const get_page_route = (pageVm) => {
// 从 app 进入应用 ,没有 $page ,获取不到路由 ,需要获取页面 尝试从 getCurrentPages 获取也页面实例
// FIXME 尽量不使用 getCurrentPages ,大部分获取路由是从 onHide 获取 ,这时可以获取到,如果是 onload ,则可能获取不到,比如 百度
let page = pageVm.$page || (pageVm.$scope && pageVm.$scope.$page);
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
if (!page) return lastPageRoute || ''
// 如果找不到 fullPath 就取 route 的值
return page.fullPath === '/' ? page.route : (page.fullPath||page.route)
};
/**
* 获取页面实例
*/
const get_page_vm = () => {
let pages = getCurrentPages();
let $page = pages[pages.length - 1];
if (!$page) return null
return $page.$vm
};
/**
* 获取页面类型
*/
const get_page_types = (self) => {
// XXX 百度有问题 ,获取的都是 componet ,等待修复
if (self.mpType === 'page' || self.$mpType === 'page' || (self.$mp && self.$mp.mpType === 'page') || self
.$options.mpType === 'page') {
return 'page';
}
if (self.mpType === 'app' || self.$mpType === 'app' || (self.$mp && self.$mp.mpType === 'app') || self.$options
.mpType === 'app') {
return 'app'
}
return null;
};
/**
* 处理上报参数
* @param {Object} 需要处理的数据
*/
const handle_data = (statData) => {
let firstArr = [];
let contentArr = [];
let lastArr = [];
for (let i in statData) {
const rd = statData[i];
rd.forEach((elm) => {
const newData = get_splicing(elm);
if (i === 0) {
firstArr.push(newData);
} else if (i === 3) {
lastArr.push(newData);
} else {
contentArr.push(newData);
}
});
}
firstArr.push(...contentArr, ...lastArr);
// 参数需要处理成字符串,方便上传
return JSON.stringify(firstArr)
};
/**
* 自定义事件参数校验
*/
const calibration = (eventName, options) => {
// login 、 share 、pay_success 、pay_fail 、register 、title
if (!eventName) {
console.error(`uni.report Missing [eventName] parameter`);
return true
}
if (typeof eventName !== 'string') {
console.error(`uni.report [eventName] Parameter type error, it can only be of type String`);
return true
}
if (eventName.length > 255) {
console.error(`uni.report [eventName] Parameter length cannot be greater than 255`);
return true
}
if (typeof options !== 'string' && typeof options !== 'object') {
console.error('uni.report [options] Parameter type error, Only supports String or Object type');
return true
}
if (typeof options === 'string' && options.length > 255) {
console.error(`uni.report [options] Parameter length cannot be greater than 255`);
return true
}
if (eventName === 'title' && typeof options !== 'string') {
console.error(
`uni.report [eventName] When the parameter is title, the [options] parameter can only be of type String`
);
return true
}
};
const get_page_name = (routepath) => {
return (titleJsons && titleJsons[routepath]) || ''
};
const Report_Data_Time = 'Report_Data_Time';
const Report_Status = 'Report_Status';
const is_report_data = () => {
return new Promise((resolve, reject) => {
let start_time = '';
let end_time = new Date().getTime();
let diff_time = DIFF_TIME;
let report_status = 1;
try {
start_time = uni.getStorageSync(Report_Data_Time);
report_status = uni.getStorageSync(Report_Status);
} catch (e) {
start_time = '';
report_status = 1;
}
if (report_status === '') {
requestData(({ enable }) => {
uni.setStorageSync(Report_Data_Time, end_time);
uni.setStorageSync(Report_Status, enable);
if (enable === 1) {
resolve();
}
});
return
}
if (report_status === 1) {
resolve();
}
if (!start_time) {
uni.setStorageSync(Report_Data_Time, end_time);
start_time = end_time;
}
if (end_time - start_time > diff_time) {
requestData(({ enable }) => {
uni.setStorageSync(Report_Data_Time, end_time);
uni.setStorageSync(Report_Status, enable);
});
}
})
};
const requestData = (done) => {
const appid = process.env.UNI_APP_ID;
let formData = {
usv: STAT_VERSION,
conf: JSON.stringify({
ak: appid,
}),
};
uni.request({
url: STAT_URL,
method: 'GET',
data: formData,
success: (res) => {
const { data } = res;
if (data.ret === 0) {
typeof done === 'function' &&
done({
enable: data.enable,
});
}
},
fail: (e) => {
let report_status_code = 1;
try {
report_status_code = uni.getStorageSync(Report_Status);
} catch (e) {
report_status_code = 1;
}
if (report_status_code === '') {
report_status_code = 1;
}
typeof done === 'function' &&
done({
enable: report_status_code,
});
},
});
};
let data = uni.getStorageSync('$$STAT__DBDATA') || {};
const dbSet = (name, value) => {
if (!data) {
data = {};
}
data[name] = value;
uni.setStorageSync('$$STAT__DBDATA', data);
};
const dbGet = (name) => {
if (!data[name]) {
let dbdata = uni.getStorageSync('$$STAT__DBDATA');
if (!dbdata) {
dbdata = {};
}
if (!dbdata[name]) {
return undefined
}
data[name] = dbdata[name];
}
return data[name]
};
const dbRemove = (name) => {
if (data[name]) {
delete data[name];
uni.setStorageSync('$$STAT__DBDATA', data);
} else {
data = uni.getStorageSync('$$STAT__DBDATA');
if (data[name]) {
delete data[name];
uni.setStorageSync('$$STAT__DBDATA', data);
}
}
};
// 首次访问时间
const FIRST_VISIT_TIME_KEY = '__first__visit__time';
// 最后访问时间
const LAST_VISIT_TIME_KEY = '__last__visit__time';
/**
* 获取当前时间
*/
const get_time = () => {
return parseInt(new Date().getTime() / 1000)
};
/**
* 获取首次访问时间
*/
const get_first_visit_time = () => {
const timeStorge = dbGet(FIRST_VISIT_TIME_KEY);
let time = 0;
if (timeStorge) {
time = timeStorge;
} else {
time = get_time();
dbSet(FIRST_VISIT_TIME_KEY, time);
// 首次访问需要 将最后访问时间置 0
dbRemove(LAST_VISIT_TIME_KEY);
}
return time
};
/**
* 最后访问时间
*/
const get_last_visit_time = () => {
const timeStorge = dbGet(LAST_VISIT_TIME_KEY);
let time = 0;
if (timeStorge) {
time = timeStorge;
}
dbSet(LAST_VISIT_TIME_KEY, get_time());
return time
};
// 页面停留时间记录key
const PAGE_RESIDENCE_TIME = '__page__residence__time';
let First_Page_Residence_Time = 0;
let Last_Page_Residence_Time = 0;
/**
* 设置页面停留时间
*/
const set_page_residence_time = () => {
First_Page_Residence_Time = get_time();
dbSet(PAGE_RESIDENCE_TIME, First_Page_Residence_Time);
return First_Page_Residence_Time
};
/**
* 获取页面停留时间
*/
const get_page_residence_time = () => {
Last_Page_Residence_Time = get_time();
First_Page_Residence_Time = dbGet(PAGE_RESIDENCE_TIME);
return Last_Page_Residence_Time - First_Page_Residence_Time
};
/**
* 获取总访问次数
*/
const TOTAL_VISIT_COUNT = '__total__visit__count';
const get_total_visit_count = () => {
const timeStorge = dbGet(TOTAL_VISIT_COUNT);
let count = 1;
if (timeStorge) {
count = timeStorge;
count++;
}
dbSet(TOTAL_VISIT_COUNT, count);
return count
};
let Set__First__Time = 0;
let Set__Last__Time = 0;
/**
* 获取第一次时间
*/
const get_first_time = () => {
let time = new Date().getTime();
Set__First__Time = time;
Set__Last__Time = 0;
return time
};
/**
* 获取最后一次时间
*/
const get_last_time = () => {
let time = new Date().getTime();
Set__Last__Time = time;
return time
};
/**
* 获取页面 \ 应用停留时间
*/
const get_residence_time = (type) => {
let residenceTime = 0;
if (Set__First__Time !== 0) {
residenceTime = Set__Last__Time - Set__First__Time;
}
residenceTime = parseInt(residenceTime / 1000);
residenceTime = residenceTime < 1 ? 1 : residenceTime;
if (type === 'app') {
let overtime = residenceTime > APP_PVER_TIME ? true : false;
return {
residenceTime,
overtime,
}
}
if (type === 'page') {
let overtime = residenceTime > PAGE_PVER_TIME ? true : false;
return {
residenceTime,
overtime,
}
}
return {
residenceTime,
}
};
// 统计数据默认值
let statData = {
uuid: get_uuid(), // 设备标识
ut: get_platform_name(), // 平台类型
mpn: get_pack_name(), // 原生平台包名、小程序 appid
ak: stat_config.appid, // uni-app 应用 Appid
usv: STAT_VERSION, // 统计 sdk 版本
v: get_version(), // 应用版本,仅app
ch: get_channel(), // 渠道信息
cn: '', // 国家
pn: '', // 省份
ct: '', // 城市
t: get_time(), // 上报数据时的时间戳
tt: '',
p: sys.platform === 'android' ? 'a' : 'i', // 手机系统
brand: sys.brand || '', // 手机品牌
md: sys.model, // 手机型号
sv: sys.system.replace(/(Android|iOS)\s/, ''), // 手机系统版本
mpsdk: sys.SDKVersion || '', // x程序 sdk version
mpv: sys.version || '', // 小程序平台版本 ,如微信、支付宝
lang: sys.language, // 语言
pr: sys.pixelRatio, // pixelRatio 设备像素比
ww: sys.windowWidth, // windowWidth 可使用窗口宽度
wh: sys.windowHeight, // windowHeight 可使用窗口高度
sw: sys.screenWidth, // screenWidth 屏幕宽度
sh: sys.screenHeight, // screenHeight 屏幕高度
};
class Report {
constructor() {
// 页面实例
this.self = '';
// 进入应用标识
this.__licationShow = false;
// 离开应用标识
this.__licationHide = false;
// 统计默认值
this.statData = statData;
// 标题默认值
this._navigationBarTitle = {
config: '',
page: '',
report: '',
lt: '',
};
// 页面参数
this._query = {};
// 页面最后停留页面的 url
// this._lastPageRoute = ''
// 注册拦截器
let registerInterceptor = typeof uni.addInterceptor === 'function';
if (registerInterceptor) {
this.addInterceptorInit();
this.interceptLogin();
this.interceptShare(true);
this.interceptRequestPayment();
}
}
addInterceptorInit() {
let self = this;
uni.addInterceptor('setNavigationBarTitle', {
invoke(args) {
self._navigationBarTitle.page = args.title;
},
});
}
interceptLogin() {
let self = this;
uni.addInterceptor('login', {
complete() {
self._login();
},
});
}
interceptShare(type) {
let self = this;
if (!type) {
self._share();
return
}
uni.addInterceptor('share', {
success() {
self._share();
},
fail() {
self._share();
},
});
}
interceptRequestPayment() {
let self = this;
uni.addInterceptor('requestPayment', {
success() {
self._payment('pay_success');
},
fail() {
self._payment('pay_fail');
},
});
}
_login() {
this.sendEventRequest({
key: 'login',
},
0
);
}
_share() {
this.sendEventRequest({
key: 'share',
}, 0);
}
_payment(key) {
this.sendEventRequest({
key,
}, 0);
}
/**
* 进入应用触发
*/
applicationShow() {
// 通过 __licationHide 判断保证是进入后台后在次进入应用,避免重复上报数据
if (this.__licationHide) {
get_last_time();
const time = get_residence_time('app');
// 需要判断进入后台是否超过时限 ,默认是 30min ,是的话需要执行进入应用的上报
if (time.overtime) {
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
let options = {
path: lastPageRoute,
scene: this.statData.sc,
};
this.sendReportRequest(options);
}
// 状态重置
this.__licationHide = false;
}
}
/**
* 离开应用触发
* @param {Object} self
* @param {Object} type
*/
applicationHide(self, type) {
if(!self){
// 表示应用切换到后台 ,此时需要从页面栈获取页面实例
self = get_page_vm();
}
// 进入应用后台保存状态,方便进入前台后判断是否上报应用数据
this.__licationHide = true;
get_last_time();
const time = get_residence_time();
const route = get_page_route(self);
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
this.sendHideRequest({
urlref: route,
urlref_ts: time.residenceTime,
},
type
);
// 重置时间
get_first_time();
}
/**
* 进入页面触发
*/
pageShow(self) {
// 清空值 ,初始化 ,避免污染后面的上报数据
this._navigationBarTitle = {
config: '',
page: '',
report: '',
lt: '',
};
const route = get_page_route(self);
const routepath = get_route(self);
this._navigationBarTitle.config = get_page_name(routepath);
// 表示应用触发 ,页面切换不触发之后的逻辑
if (this.__licationShow) {
get_first_time();
// this._lastPageRoute = route
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
this.__licationShow = false;
return
}
get_last_time();
const time = get_residence_time('page');
// 停留时间
if (time.overtime) {
let options = {
path: route,
scene: this.statData.sc,
};
this.sendReportRequest(options);
}
// 重置时间
get_first_time();
}
/**
* 离开页面触发
*/
pageHide(self) {
if (!this.__licationHide) {
get_last_time();
const time = get_residence_time('page');
let route = get_page_route(self);
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
if (!lastPageRoute) {
lastPageRoute = route;
}
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
this.sendPageRequest({
url: route,
urlref: lastPageRoute,
urlref_ts: time.residenceTime,
});
// this._lastPageRoute = route
return
}
}
/**
* 发送请求,应用维度上报
* @param {Object} options 页面信息
*/
sendReportRequest(options) {
this._navigationBarTitle.lt = '1';
this._navigationBarTitle.config = get_page_name(options.path);
let is_opt = options.query && JSON.stringify(options.query) !== '{}';
let query = is_opt ? '?' + JSON.stringify(options.query) : '';
Object.assign(this.statData, {
lt: '1',
url: (options.path + query) || '',
t: get_time(),
sc: get_scene(options.scene),
fvts: get_first_visit_time(),
lvts: get_last_visit_time(),
tvc: get_total_visit_count()
});
if (get_platform_name() === 'n') {
this.getProperty();
} else {
this.getNetworkInfo();
}
}
/**
* 发送请求,页面维度上报
* @param {Object} opt
*/
sendPageRequest(opt) {
let {
url,
urlref,
urlref_ts
} = opt;
this._navigationBarTitle.lt = '11';
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '11',
ut: this.statData.ut,
url,
tt: this.statData.tt,
urlref,
urlref_ts,
ch: this.statData.ch,
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options);
}
/**
* 进入后台上报数据
* @param {Object} opt
* @param {Object} type
*/
sendHideRequest(opt, type) {
let {
urlref,
urlref_ts
} = opt;
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '3',
ut: this.statData.ut,
urlref,
urlref_ts,
ch: this.statData.ch,
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options, type);
}
/**
* 自定义事件上报
*/
sendEventRequest({
key = '',
value = ''
} = {}) {
// const route = this._lastPageRoute
const routepath = get_route();
this._navigationBarTitle.config = get_page_name(routepath);
this._navigationBarTitle.lt = '21';
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '21',
ut: this.statData.ut,
url: routepath,
ch: this.statData.ch,
e_n: key,
e_v: typeof value === 'object' ? JSON.stringify(value) : value.toString(),
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options);
}
/**
* 获取wgt资源版本
*/
getProperty() {
plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
this.statData.v = wgtinfo.version || '';
this.getNetworkInfo();
});
}
/**
* 获取网络信息
*/
getNetworkInfo() {
uni.getNetworkType({
success: (result) => {
this.statData.net = result.networkType;
this.getLocation();
},
});
}
/**
* 获取位置信息
*/
getLocation() {
if (stat_config.getLocation) {
uni.getLocation({
type: 'wgs84',
geocode: true,
success: (result) => {
if (result.address) {
this.statData.cn = result.address.country;
this.statData.pn = result.address.province;
this.statData.ct = result.address.city;
}
this.statData.lat = result.latitude;
this.statData.lng = result.longitude;
this.request(this.statData);
},
});
} else {
this.statData.lat = 0;
this.statData.lng = 0;
this.request(this.statData);
}
}
/**
* 发送请求
* @param {Object} data 上报数据
* @param {Object} type 类型
*/
request(data, type) {
let time = get_time();
const title = this._navigationBarTitle;
Object.assign(data, {
ttn: title.page,
ttpj: title.config,
ttc: title.report
});
let uniStatData = dbGet('__UNI__STAT__DATA') || {};
if (!uniStatData[data.lt]) {
uniStatData[data.lt] = [];
}
// 加入队列
uniStatData[data.lt].push(data);
dbSet('__UNI__STAT__DATA', uniStatData);
let page_residence_time = get_page_residence_time();
// 判断时候到达上报时间 ,默认 10 秒上报
if (page_residence_time < OPERATING_TIME && !type) return
// 时间超过,重新获取时间戳
set_page_residence_time();
const stat_data = handle_data(uniStatData);
let optionsData = {
usv: STAT_VERSION, //统计 SDK 版本号
t: time, //发送请求时的时间戮
requests: stat_data,
};
// 重置队列
dbRemove('__UNI__STAT__DATA');
// XXX 安卓需要延迟上报 ,否则会有未知错误,需要验证处理
if (get_platform_name() === 'n' && this.statData.p === 'a') {
setTimeout(() => {
this.sendRequest(optionsData);
}, 200);
return
}
this.sendRequest(optionsData);
}
getIsReportData(){
return is_report_data()
}
/**
* 数据上报
* @param {Object} optionsData 需要上报的数据
*/
sendRequest(optionsData) {
{
if (!uniCloud.config) {
console.error('当前尚未绑定服务空间.');
return
}
uniCloud.callFunction({
name: 'uni-stat-report',
data: optionsData,
success: (res) => {},
fail: (err) => {}
});
}
}
/**
* h5 请求
*/
imageRequest(data) {
this.getIsReportData().then(() => {
let image = new Image();
let options = get_sgin(get_encodeURIComponent_options(data)).options;
image.src = STAT_H5_URL + '?' + options;
});
}
sendEvent(key, value) {
// 校验 type 参数
if (calibration(key, value)) return
if (key === 'title') {
this._navigationBarTitle.report = value;
return
}
this.sendEventRequest({
key,
value: typeof value === 'object' ? JSON.stringify(value) : value,
},
1
);
}
}
class Stat extends Report {
static getInstance() {
if (!uni.__stat_instance) {
uni.__stat_instance = new Stat();
}
return uni.__stat_instance
}
constructor() {
super();
}
/**
* 进入应用
* @param {Object} options 页面参数
* @param {Object} self 当前页面实例
*/
launch(options, self) {
// 初始化页面停留时间 start
set_page_residence_time();
this.__licationShow = true;
this.sendReportRequest(options, true);
}
load(options, self) {
this.self = self;
this._query = options;
}
appHide(self){
this.applicationHide(self, true);
}
appShow(self){
this.applicationShow(self);
}
show(self) {
this.self = self;
if (get_page_types(self) === 'page') {
this.pageShow(self);
}
// #ifdef VUE3
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
if (get_page_types(self) === 'app') {
this.appShow();
}
}
// #endif
// #ifndef VUE3
if (get_page_types(self) === 'app') {
this.appShow();
}
// #endif
}
hide(self) {
this.self = self;
if (get_page_types(self) === 'page') {
this.pageHide(self);
}
// #ifdef VUE3
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
if (get_page_types(self) === 'app') {
this.appHide();
}
}
// #endif
// #ifndef VUE3
if (get_page_types(self) === 'app') {
this.appHide();
}
// #endif
}
error(em) {
// 开发工具内不上报错误
if (this._platform === 'devtools') {
if (process.env.NODE_ENV === 'development') {
console.info('当前运行环境为开发者工具,不上报数据。');
return;
}
}
let emVal = '';
if (!em.message) {
emVal = JSON.stringify(em);
} else {
emVal = em.stack;
}
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '31',
ut: this.statData.ut,
ch: this.statData.ch,
mpsdk: this.statData.mpsdk,
mpv: this.statData.mpv,
v: this.statData.v,
em: emVal,
usv: this.statData.usv,
t: parseInt(new Date().getTime() / 1000),
p: this.statData.p,
};
this.request(options);
}
}
var Stat$1 = Stat;
const stat = Stat$1.getInstance();
// 用于判断是隐藏页面还是卸载页面
let isHide = false;
const lifecycle = {
onLaunch(options) {
// 进入应用上报数据
stat.launch(options, this);
},
onLoad(options) {
stat.load(options, this);
// 重写分享,获取分享上报事件
if (this.$scope && this.$scope.onShareAppMessage) {
let oldShareAppMessage = this.$scope.onShareAppMessage;
this.$scope.onShareAppMessage = function(options) {
stat.interceptShare(false);
return oldShareAppMessage.call(this, options)
};
}
},
onShow() {
isHide = false;
stat.show(this);
},
onHide() {
isHide = true;
stat.hide(this);
},
onUnload() {
if (isHide) {
isHide = false;
return
}
stat.hide(this);
},
onError(e) {
stat.error(e);
}
};
function main() {
{
console.log('uni统计开启,version:2');
}
if (process.env.NODE_ENV === 'development') {
uni.report = function(type, options) {};
} else {
// #ifdef VUE3
uni.onCreateVueApp((app) => {
app.mixin(lifecycle);
uni.report = function(type, options) {
stat.sendEvent(type, options);
};
});
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
uni.onAppHide(() => {
stat.appHide(get_page_vm());
});
uni.onAppShow(() => {
stat.appShow(get_page_vm());
});
}
// #endif
// #ifndef VUE3
const Vue = require('vue');
(Vue.default || Vue).mixin(lifecycle);
uni.report = function(type, options) {
stat.sendEvent(type, options);
};
// #endif
}
}
main();
import pagesTitle from 'uni-pages?{"type":"style"}';
import Vue from 'vue';
/** /**
* 获取系统信息 * 获取系统信息
*/ */
...@@ -8,70 +5,98 @@ const sys = uni.getSystemInfoSync(); ...@@ -8,70 +5,98 @@ const sys = uni.getSystemInfoSync();
// 访问开始即启动小程序,访问结束结分为:进入后台超过5min、在前台无任何操作超过30min、在新的来源打开小程序; // 访问开始即启动小程序,访问结束结分为:进入后台超过5min、在前台无任何操作超过30min、在新的来源打开小程序;
const STAT_VERSION = '0.0.1'; const STAT_VERSION = '0.0.1';
const STAT_URL = 'https://tongji.dcloud.io/uni/stat'; const STAT_URL = 'https://tongji.dcloud.io/uni/stat';
const STAT_H5_URL = 'https://tongji.dcloud.io/uni/stat.gif'; const STAT_H5_URL = 'https://tongji.dcloud.io/uni/stat.gif';
const PAGE_PVER_TIME = 1800; // 页面在前台无操作结束访问时间 单位s const PAGE_PVER_TIME = 1800; // 页面在前台无操作结束访问时间 单位s
const APP_PVER_TIME = 300; // 应用在后台结束访问时间 单位s const APP_PVER_TIME = 300; // 应用在后台结束访问时间 单位s
const OPERATING_TIME = 10; // 数据上报时间 单位s const OPERATING_TIME = 10; // 数据上报时间 单位s
const DIFF_TIME = 60 * 1000 * 60 * 24; const DIFF_TIME = 60 * 1000 * 60 * 24;
let pagesData = pagesTitle.pages; let statConfig = {
appid: ''
};
let titleJsons = {}; let titleJsons = {};
for (let i in pagesData) {
titleJsons[i] = pagesData[i].navigationBarTitleText || '';
}
const UUID_KEY = '__DC_STAT_UUID';
const UUID_VALUE = '__DC_UUID_VALUE';
function getUuid() {
let uuid = '';
if (get_platform_name() === 'n') {
try {
uuid = plus.runtime.getDCloudId();
} catch (e) {
uuid = '';
}
return uuid
}
try { // #ifdef VUE3
uuid = uni.getStorageSync(UUID_KEY); statConfig.appid = process.env.UNI_APP_ID;
} catch (e) { titleJsons = process.env.UNI_STAT_TITLE_JSON;
uuid = UUID_VALUE; // #endif
} // #ifndef VUE3
statConfig = require('uni-stat-config').default || require('uni-stat-config');
const pagesTitle = require('uni-pages?{"type":"style"}').default;
let pagesData = pagesTitle.pages;
for (let i in pagesData) {
const style = pagesData[i];
const titleText =
// MP
style.navigationBarTitleText ||
// ali
style.defaultTitle||
// H5 || App
style.navigationBar?.titleText ||
'';
if (titleText) {
titleJsons[i] = titleText;
}
}
// #endif
if (!uuid) {
uuid = Date.now() + '' + Math.floor(Math.random() * 1e7); const UUID_KEY = '__DC_STAT_UUID';
try { const UUID_VALUE = '__DC_UUID_VALUE';
uni.setStorageSync(UUID_KEY, uuid);
} catch (e) { function getUuid() {
uni.setStorageSync(UUID_KEY, UUID_VALUE); let uuid = '';
} if (get_platform_name() === 'n') {
} try {
return uuid uuid = plus.runtime.getDCloudId();
} catch (e) {
uuid = '';
}
return uuid
}
try {
uuid = uni.getStorageSync(UUID_KEY);
} catch (e) {
uuid = UUID_VALUE;
}
if (!uuid) {
uuid = Date.now() + '' + Math.floor(Math.random() * 1e7);
try {
uni.setStorageSync(UUID_KEY, uuid);
} catch (e) {
uni.setStorageSync(UUID_KEY, UUID_VALUE);
}
}
return uuid
} }
/**
const get_uuid = (statData) => { * 获取配置信息 如 appid
// 有可能不存在 deviceId(一般不存在就是出bug了),就自己生成一个 */
return sys.deviceId || getUuid() const stat_config = statConfig;
};
const get_uuid = (statData) => {
const get_sgin = (statData) => { // 有可能不存在 deviceId(一般不存在就是出bug了),就自己生成一个
let arr = Object.keys(statData); return sys.deviceId || getUuid()
let sortArr = arr.sort(); };
let sgin = {};
let sginStr = ''; const get_sgin = (statData) => {
for (var i in sortArr) { let arr = Object.keys(statData);
sgin[sortArr[i]] = statData[sortArr[i]]; let sortArr = arr.sort();
sginStr += sortArr[i] + '=' + statData[sortArr[i]] + '&'; let sgin = {};
} let sginStr = '';
for (var i in sortArr) {
return { sgin[sortArr[i]] = statData[sortArr[i]];
sign: '', sginStr += sortArr[i] + '=' + statData[sortArr[i]] + '&';
options: sginStr.substr(0, sginStr.length - 1), }
}
}; return {
sign: '',
options: sginStr.substr(0, sginStr.length - 1),
}
};
const get_encodeURIComponent_options = (statData) => { const get_encodeURIComponent_options = (statData) => {
let data = {}; let data = {};
...@@ -80,234 +105,233 @@ const get_encodeURIComponent_options = (statData) => { ...@@ -80,234 +105,233 @@ const get_encodeURIComponent_options = (statData) => {
} }
return data return data
}; };
/** /**
* 获取当前平台 * 获取当前平台
* 移动端 : 'n', * 移动端 : 'n',
* h5 : 'h5', * h5 : 'h5',
* 微信 : 'wx', * 微信 : 'wx',
* 阿里 : 'ali', * 阿里 : 'ali',
* 百度 : 'bd', * 百度 : 'bd',
* 头条 : 'tt', * 头条 : 'tt',
* qq : 'qq', * qq : 'qq',
* 快应用 : 'qn', * 快应用 : 'qn',
* 快手 : 'ks', * 快手 : 'ks',
* 飞书 : 'lark', * 飞书 : 'lark',
* 快应用 : 'qw', * 快应用 : 'qw',
* 钉钉 : 'dt' * 钉钉 : 'dt'
*/ */
const get_platform_name = () => { const get_platform_name = () => {
// 苹果审核代码中禁止出现 alipay 字样 ,需要特殊处理一下 // 苹果审核代码中禁止出现 alipay 字样 ,需要特殊处理一下
const aliArr = ['y', 'a', 'p', 'mp-ali']; const aliArr = ['y', 'a', 'p', 'mp-ali'];
const platformList = { const platformList = {
'app': 'n', 'app': 'n',
'app-plus': 'n', 'app-plus': 'n',
h5: 'h5', h5: 'h5',
'mp-weixin': 'wx', 'mp-weixin': 'wx',
[aliArr.reverse().join('')]: 'ali', [aliArr.reverse().join('')]: 'ali',
'mp-baidu': 'bd', 'mp-baidu': 'bd',
'mp-toutiao': 'tt', 'mp-toutiao': 'tt',
'mp-qq': 'qq', 'mp-qq': 'qq',
'quickapp-native': 'qn', 'quickapp-native': 'qn',
'mp-kuaishou': 'ks', 'mp-kuaishou': 'ks',
'mp-xhs': 'xhs', 'mp-lark': 'lark',
'mp-lark': 'lark', 'quickapp-webview':'qw'
'quickapp-webview': 'qw' };
}; if(platformList[process.env.VUE_APP_PLATFORM] === 'ali'){
if (platformList[process.env.VUE_APP_PLATFORM] === 'ali') { if(my&&my.env){
if (my && my.env) { const clientName = my.env.clientName;
const clientName = my.env.clientName; if(clientName === 'ap') return 'ali'
if (clientName === 'ap') return 'ali' if(clientName === 'dingtalk') return 'dt'
if (clientName === 'dingtalk') return 'dt' // TODO 缺少 ali 下的其他平台
// TODO 缺少 ali 下的其他平台 }
} }
} return platformList[process.env.VUE_APP_PLATFORM]
return platformList[process.env.VUE_APP_PLATFORM] };
};
/**
/** * 获取小程序 appid
* 获取小程序 appid */
*/ const get_pack_name = () => {
const get_pack_name = () => { let packName = '';
let packName = ''; if (get_platform_name() === 'wx' || get_platform_name() === 'qq') {
if (get_platform_name() === 'wx' || get_platform_name() === 'qq') { // 兼容微信小程序低版本基础库
// 兼容微信小程序低版本基础库 if (uni.canIUse('getAccountInfoSync')) {
if (uni.canIUse('getAccountInfoSync')) { packName = uni.getAccountInfoSync().miniProgram.appId || '';
packName = uni.getAccountInfoSync().miniProgram.appId || ''; }
} }
} if (get_platform_name() === 'n') ;
if (get_platform_name() === 'n') ; return packName
return packName };
};
/**
/** * 应用版本
* 应用版本 */
*/ const get_version = () => {
const get_version = () => { return get_platform_name() === 'n' ? plus.runtime.version : ''
return get_platform_name() === 'n' ? plus.runtime.version : '' };
};
/**
/** * 获取渠道
* 获取渠道 */
*/ const get_channel = () => {
const get_channel = () => { const platformName = get_platform_name();
const platformName = get_platform_name(); let channel = '';
let channel = ''; if (platformName === 'n') {
if (platformName === 'n') { channel = plus.runtime.channel;
channel = plus.runtime.channel; }
} return channel
return channel };
};
/**
/** * 获取小程序场景值
* 获取小程序场景值 * @param {Object} options 页面信息
* @param {Object} options 页面信息 */
*/ const get_scene = (options) => {
const get_scene = (options) => { const platformName = get_platform_name();
const platformName = get_platform_name(); let scene = '';
let scene = ''; if (options) {
if (options) { return options
return options }
} if (platformName === 'wx') {
if (platformName === 'wx') { scene = uni.getLaunchOptionsSync().scene;
scene = uni.getLaunchOptionsSync().scene; }
} return scene
return scene };
};
/**
/** * 获取拼接参数
* 获取拼接参数 */
*/ const get_splicing = (data) => {
const get_splicing = (data) => { let str = '';
let str = ''; for (var i in data) {
for (var i in data) { str += i + '=' + data[i] + '&';
str += i + '=' + data[i] + '&'; }
} return str.substr(0, str.length - 1)
return str.substr(0, str.length - 1) };
};
/**
/** * 获取页面url,不包含参数
* 获取页面url,不包含参数 */
*/ const get_route = (pageVm) => {
const get_route = (pageVm) => { let _self = pageVm || get_page_vm();
let _self = pageVm || get_page_vm(); if (get_platform_name() === 'bd') {
if (get_platform_name() === 'bd') { let mp_route = _self.$mp && _self.$mp.page && _self.$mp.page.is;
let mp_route = _self.$mp && _self.$mp.page && _self.$mp.page.is; let scope_route = _self.$scope && _self.$scope.is;
let scope_route = _self.$scope && _self.$scope.is; return mp_route || scope_route || ''
return mp_route || scope_route || '' } else {
} else { return _self.route || (_self.$scope && _self.$scope.route) || (_self.$mp && _self.$mp.page.route)
return _self.route || (_self.$scope && _self.$scope.route) || (_self.$mp && _self.$mp.page.route) }
} };
};
/**
/** * 获取页面url, 包含参数
* 获取页面url, 包含参数 */
*/ const get_page_route = (pageVm) => {
const get_page_route = (pageVm) => { // 从 app 进入应用 ,没有 $page ,获取不到路由 ,需要获取页面 尝试从 getCurrentPages 获取也页面实例
// 从 app 进入应用 ,没有 $page ,获取不到路由 ,需要获取页面 尝试从 getCurrentPages 获取也页面实例 // FIXME 尽量不使用 getCurrentPages ,大部分获取路由是从 onHide 获取 ,这时可以获取到,如果是 onload ,则可能获取不到,比如 百度
// FIXME 尽量不使用 getCurrentPages ,大部分获取路由是从 onHide 获取 ,这时可以获取到,如果是 onload ,则可能获取不到,比如 百度
let page = pageVm.$page || (pageVm.$scope && pageVm.$scope.$page);
let page = pageVm.$page || (pageVm.$scope && pageVm.$scope.$page); let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
if (!page) return lastPageRoute || '' if (!page) return lastPageRoute || ''
return page.fullPath === '/' ? page.route : page.fullPath // 如果找不到 fullPath 就取 route 的值
}; return page.fullPath === '/' ? page.route : (page.fullPath||page.route)
};
/** /**
* 获取页面实例 * 获取页面实例
*/ */
const get_page_vm = () => { const get_page_vm = () => {
let pages = getCurrentPages(); let pages = getCurrentPages();
let $page = pages[pages.length - 1]; let $page = pages[pages.length - 1];
if (!$page) return null if (!$page) return null
return $page.$vm return $page.$vm
}; };
/** /**
* 获取页面类型 * 获取页面类型
*/ */
const get_page_types = (self) => { const get_page_types = (self) => {
// XXX 百度有问题 ,获取的都是 componet ,等待修复 // XXX 百度有问题 ,获取的都是 componet ,等待修复
if (self.mpType === 'page' || self.$mpType === 'page' || (self.$mp && self.$mp.mpType === 'page') || self if (self.mpType === 'page' || self.$mpType === 'page' || (self.$mp && self.$mp.mpType === 'page') || self
.$options.mpType === 'page') { .$options.mpType === 'page') {
return 'page'; return 'page';
} }
if (self.mpType === 'app' || self.$mpType === 'app' || (self.$mp && self.$mp.mpType === 'app') || self.$options if (self.mpType === 'app' || self.$mpType === 'app' || (self.$mp && self.$mp.mpType === 'app') || self.$options
.mpType === 'app') { .mpType === 'app') {
return 'app' return 'app'
} }
return null; return null;
}; };
/** /**
* 处理上报参数 * 处理上报参数
* @param {Object} 需要处理的数据 * @param {Object} 需要处理的数据
*/ */
const handle_data = (statData) => { const handle_data = (statData) => {
let firstArr = []; let firstArr = [];
let contentArr = []; let contentArr = [];
let lastArr = []; let lastArr = [];
for (let i in statData) { for (let i in statData) {
const rd = statData[i]; const rd = statData[i];
rd.forEach((elm) => { rd.forEach((elm) => {
const newData = get_splicing(elm); const newData = get_splicing(elm);
if (i === 0) { if (i === 0) {
firstArr.push(newData); firstArr.push(newData);
} else if (i === 3) { } else if (i === 3) {
lastArr.push(newData); lastArr.push(newData);
} else { } else {
contentArr.push(newData); contentArr.push(newData);
} }
}); });
} }
firstArr.push(...contentArr, ...lastArr); firstArr.push(...contentArr, ...lastArr);
// 参数需要处理成字符串,方便上传 // 参数需要处理成字符串,方便上传
return JSON.stringify(firstArr) return JSON.stringify(firstArr)
}; };
/** /**
* 自定义事件参数校验 * 自定义事件参数校验
*/ */
const calibration = (eventName, options) => { const calibration = (eventName, options) => {
// login 、 share 、pay_success 、pay_fail 、register 、title // login 、 share 、pay_success 、pay_fail 、register 、title
if (!eventName) { if (!eventName) {
console.error(`uni.report Missing [eventName] parameter`); console.error(`uni.report Missing [eventName] parameter`);
return true return true
} }
if (typeof eventName !== 'string') { if (typeof eventName !== 'string') {
console.error(`uni.report [eventName] Parameter type error, it can only be of type String`); console.error(`uni.report [eventName] Parameter type error, it can only be of type String`);
return true return true
} }
if (eventName.length > 255) { if (eventName.length > 255) {
console.error(`uni.report [eventName] Parameter length cannot be greater than 255`); console.error(`uni.report [eventName] Parameter length cannot be greater than 255`);
return true return true
} }
if (typeof options !== 'string' && typeof options !== 'object') { if (typeof options !== 'string' && typeof options !== 'object') {
console.error('uni.report [options] Parameter type error, Only supports String or Object type'); console.error('uni.report [options] Parameter type error, Only supports String or Object type');
return true return true
} }
if (typeof options === 'string' && options.length > 255) { if (typeof options === 'string' && options.length > 255) {
console.error(`uni.report [options] Parameter length cannot be greater than 255`); console.error(`uni.report [options] Parameter length cannot be greater than 255`);
return true return true
} }
if (eventName === 'title' && typeof options !== 'string') { if (eventName === 'title' && typeof options !== 'string') {
console.error( console.error(
`uni.report [eventName] When the parameter is title, the [options] parameter can only be of type String` `uni.report [eventName] When the parameter is title, the [options] parameter can only be of type String`
); );
return true return true
} }
}; };
const get_page_name = (routepath) => { const get_page_name = (routepath) => {
return (titleJsons && titleJsons[routepath]) || '' return (titleJsons && titleJsons[routepath]) || ''
}; };
const Report_Data_Time = 'Report_Data_Time'; const Report_Data_Time = 'Report_Data_Time';
const Report_Status = 'Report_Status'; const Report_Status = 'Report_Status';
const is_report_data = () => { const is_report_data = () => {
...@@ -392,8 +416,8 @@ const requestData = (done) => { ...@@ -392,8 +416,8 @@ const requestData = (done) => {
}); });
}; };
const dbSet = (name, value) => { let data = uni.getStorageSync('$$STAT__DBDATA') || {};
let data = uni.getStorageSync('$$STAT__DBDATA') || {}; const dbSet = (name, value) => {
if (!data) { if (!data) {
data = {}; data = {};
} }
...@@ -401,19 +425,21 @@ const dbSet = (name, value) => { ...@@ -401,19 +425,21 @@ const dbSet = (name, value) => {
uni.setStorageSync('$$STAT__DBDATA', data); uni.setStorageSync('$$STAT__DBDATA', data);
}; };
const dbGet = (name) => { const dbGet = (name) => {
let data = uni.getStorageSync('$$STAT__DBDATA') || {};
if (!data) {
data = {};
}
if (!data[name]) { if (!data[name]) {
return undefined let dbdata = uni.getStorageSync('$$STAT__DBDATA');
if (!dbdata) {
dbdata = {};
}
if (!dbdata[name]) {
return undefined
}
data[name] = dbdata[name];
} }
return data[name] return data[name]
}; };
const dbRemove = (name) => { const dbRemove = (name) => {
let data = uni.getStorageSync('$$STAT__DBDATA') || {};
if (data[name]) { if (data[name]) {
delete data[name]; delete data[name];
uni.setStorageSync('$$STAT__DBDATA', data); uni.setStorageSync('$$STAT__DBDATA', data);
...@@ -469,12 +495,14 @@ const get_last_visit_time = () => { ...@@ -469,12 +495,14 @@ const get_last_visit_time = () => {
// 页面停留时间记录key // 页面停留时间记录key
const PAGE_RESIDENCE_TIME = '__page__residence__time'; const PAGE_RESIDENCE_TIME = '__page__residence__time';
let First_Page_Residence_Time = 0;
let Last_Page_Residence_Time = 0;
/** /**
* 设置页面停留时间 * 设置页面停留时间
*/ */
const set_page_residence_time = () => { const set_page_residence_time = () => {
let First_Page_Residence_Time = get_time(); First_Page_Residence_Time = get_time();
dbSet(PAGE_RESIDENCE_TIME, First_Page_Residence_Time); dbSet(PAGE_RESIDENCE_TIME, First_Page_Residence_Time);
return First_Page_Residence_Time return First_Page_Residence_Time
}; };
...@@ -483,8 +511,8 @@ const set_page_residence_time = () => { ...@@ -483,8 +511,8 @@ const set_page_residence_time = () => {
* 获取页面停留时间 * 获取页面停留时间
*/ */
const get_page_residence_time = () => { const get_page_residence_time = () => {
let Last_Page_Residence_Time = get_time(); Last_Page_Residence_Time = get_time();
let First_Page_Residence_Time = dbGet(PAGE_RESIDENCE_TIME); First_Page_Residence_Time = dbGet(PAGE_RESIDENCE_TIME);
return Last_Page_Residence_Time - First_Page_Residence_Time return Last_Page_Residence_Time - First_Page_Residence_Time
}; };
...@@ -555,518 +583,506 @@ const get_residence_time = (type) => { ...@@ -555,518 +583,506 @@ const get_residence_time = (type) => {
} }
}; };
let statConfig = require('uni-stat-config').default || require('uni-stat-config');
// 统计数据默认值 // 统计数据默认值
let statData = { let statData = {
uuid: get_uuid(), // 设备标识 uuid: get_uuid(), // 设备标识
ut: get_platform_name(), // 平台类型 ut: get_platform_name(), // 平台类型
mpn: get_pack_name(), // 原生平台包名、小程序 appid mpn: get_pack_name(), // 原生平台包名、小程序 appid
ak: statConfig.appid, // uni-app 应用 Appid ak: stat_config.appid, // uni-app 应用 Appid
usv: STAT_VERSION, // 统计 sdk 版本 usv: STAT_VERSION, // 统计 sdk 版本
v: get_version(), // 应用版本,仅app v: get_version(), // 应用版本,仅app
ch: get_channel(), // 渠道信息 ch: get_channel(), // 渠道信息
cn: '', // 国家 cn: '', // 国家
pn: '', // 省份 pn: '', // 省份
ct: '', // 城市 ct: '', // 城市
t: get_time(), // 上报数据时的时间戳 t: get_time(), // 上报数据时的时间戳
tt: '', tt: '',
p: sys.platform === 'android' ? 'a' : 'i', // 手机系统 p: sys.platform === 'android' ? 'a' : 'i', // 手机系统
brand: sys.brand || '', // 手机品牌 brand: sys.brand || '', // 手机品牌
md: sys.model, // 手机型号 md: sys.model, // 手机型号
sv: sys.system.replace(/(Android|iOS)\s/, ''), // 手机系统版本 sv: sys.system.replace(/(Android|iOS)\s/, ''), // 手机系统版本
mpsdk: sys.SDKVersion || '', // x程序 sdk version mpsdk: sys.SDKVersion || '', // x程序 sdk version
mpv: sys.version || '', // 小程序平台版本 ,如微信、支付宝 mpv: sys.version || '', // 小程序平台版本 ,如微信、支付宝
lang: sys.language, // 语言 lang: sys.language, // 语言
pr: sys.pixelRatio, // pixelRatio 设备像素比 pr: sys.pixelRatio, // pixelRatio 设备像素比
ww: sys.windowWidth, // windowWidth 可使用窗口宽度 ww: sys.windowWidth, // windowWidth 可使用窗口宽度
wh: sys.windowHeight, // windowHeight 可使用窗口高度 wh: sys.windowHeight, // windowHeight 可使用窗口高度
sw: sys.screenWidth, // screenWidth 屏幕宽度 sw: sys.screenWidth, // screenWidth 屏幕宽度
sh: sys.screenHeight, // screenHeight 屏幕高度 sh: sys.screenHeight, // screenHeight 屏幕高度
}; };
class Report { class Report {
constructor() { constructor() {
// 页面实例 // 页面实例
this.self = ''; this.self = '';
// 进入应用标识 // 进入应用标识
this.__licationShow = false; this.__licationShow = false;
// 离开应用标识 // 离开应用标识
this.__licationHide = false; this.__licationHide = false;
// 统计默认值 // 统计默认值
this.statData = statData; this.statData = statData;
// 标题默认值 // 标题默认值
this._navigationBarTitle = { this._navigationBarTitle = {
config: '', config: '',
page: '', page: '',
report: '', report: '',
lt: '', lt: '',
}; };
// 页面参数
this._query = {};
// 页面最后停留页面的 url
// this._lastPageRoute = ''
// 注册拦截器
let registerInterceptor = typeof uni.addInterceptor === 'function';
if (registerInterceptor) {
this.addInterceptorInit();
this.interceptLogin();
this.interceptShare(true);
this.interceptRequestPayment();
}
}
addInterceptorInit() { // 页面参数
let self = this; this._query = {};
uni.addInterceptor('setNavigationBarTitle', { // 页面最后停留页面的 url
invoke(args) { // this._lastPageRoute = ''
self._navigationBarTitle.page = args.title;
},
});
}
interceptLogin() { // 注册拦截器
let self = this; let registerInterceptor = typeof uni.addInterceptor === 'function';
uni.addInterceptor('login', { if (registerInterceptor) {
complete() { this.addInterceptorInit();
self._login(); this.interceptLogin();
}, this.interceptShare(true);
}); this.interceptRequestPayment();
} }
}
interceptShare(type) { addInterceptorInit() {
let self = this; let self = this;
if (!type) { uni.addInterceptor('setNavigationBarTitle', {
self._share(); invoke(args) {
return self._navigationBarTitle.page = args.title;
} },
uni.addInterceptor('share', { });
success() { }
self._share();
},
fail() {
self._share();
},
});
}
interceptRequestPayment() { interceptLogin() {
let self = this; let self = this;
uni.addInterceptor('requestPayment', { uni.addInterceptor('login', {
success() { complete() {
self._payment('pay_success'); self._login();
}, },
fail() { });
self._payment('pay_fail'); }
},
});
}
_login() { interceptShare(type) {
this.sendEventRequest({ let self = this;
key: 'login', if (!type) {
}, self._share();
0 return
); }
} uni.addInterceptor('share', {
success() {
self._share();
},
fail() {
self._share();
},
});
}
_share() { interceptRequestPayment() {
this.sendEventRequest({ let self = this;
key: 'share', uni.addInterceptor('requestPayment', {
}, 0); success() {
} self._payment('pay_success');
_payment(key) { },
this.sendEventRequest({ fail() {
key, self._payment('pay_fail');
}, 0); },
} });
}
/** _login() {
* 进入应用触发 this.sendEventRequest({
*/ key: 'login',
applicationShow() { },
// 通过 __licationHide 判断保证是进入后台后在次进入应用,避免重复上报数据 0
if (this.__licationHide) { );
get_last_time(); }
const time = get_residence_time('app');
// 需要判断进入后台是否超过时限 ,默认是 30min ,是的话需要执行进入应用的上报
if (time.overtime) {
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
let options = {
path: lastPageRoute,
scene: this.statData.sc,
};
this.sendReportRequest(options);
}
// 状态重置
this.__licationHide = false;
}
}
/** _share() {
* 离开应用触发 this.sendEventRequest({
* @param {Object} self key: 'share',
* @param {Object} type }, 0);
*/ }
applicationHide(self, type) { _payment(key) {
// 进入应用后台保存状态,方便进入前台后判断是否上报应用数据 this.sendEventRequest({
this.__licationHide = true; key,
get_last_time(); }, 0);
const time = get_residence_time(); }
const route = get_page_route(self);
// this._lastPageRoute = route
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
this.sendHideRequest({
urlref: route,
urlref_ts: time.residenceTime,
},
type
);
// 重置时间
get_first_time();
}
/** /**
* 进入页面触发 * 进入应用触发
*/ */
pageShow(self) { applicationShow() {
// 清空值 ,初始化 ,避免污染后面的上报数据 // 通过 __licationHide 判断保证是进入后台后在次进入应用,避免重复上报数据
this._navigationBarTitle = { if (this.__licationHide) {
config: '', get_last_time();
page: '', const time = get_residence_time('app');
report: '', // 需要判断进入后台是否超过时限 ,默认是 30min ,是的话需要执行进入应用的上报
lt: '', if (time.overtime) {
}; let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
let options = {
const route = get_page_route(self); path: lastPageRoute,
const routepath = get_route(self); scene: this.statData.sc,
};
this._navigationBarTitle.config = get_page_name(routepath); this.sendReportRequest(options);
// 表示应用触发 ,页面切换不触发之后的逻辑 }
if (this.__licationShow) { // 状态重置
get_first_time(); this.__licationHide = false;
// this._lastPageRoute = route }
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route); }
this.__licationShow = false;
return
}
get_last_time();
const time = get_residence_time('page');
// 停留时间
if (time.overtime) {
let options = {
path: route,
scene: this.statData.sc,
};
this.sendReportRequest(options);
}
// 重置时间
get_first_time();
}
/** /**
* 离开页面触发 * 离开应用触发
*/ * @param {Object} self
pageHide(self) { * @param {Object} type
if (!this.__licationHide) { */
get_last_time(); applicationHide(self, type) {
const time = get_residence_time('page'); if(!self){
let route = get_page_route(self); // 表示应用切换到后台 ,此时需要从页面栈获取页面实例
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE'); self = get_page_vm();
if (!lastPageRoute) { }
lastPageRoute = route; // 进入应用后台保存状态,方便进入前台后判断是否上报应用数据
} this.__licationHide = true;
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route); get_last_time();
this.sendPageRequest({ const time = get_residence_time();
url: route, const route = get_page_route(self);
urlref: lastPageRoute, uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
urlref_ts: time.residenceTime, this.sendHideRequest({
}); urlref: route,
// this._lastPageRoute = route urlref_ts: time.residenceTime,
return },
} type
} );
// 重置时间
get_first_time();
}
/**
* 进入页面触发
*/
pageShow(self) {
// 清空值 ,初始化 ,避免污染后面的上报数据
this._navigationBarTitle = {
config: '',
page: '',
report: '',
lt: '',
};
/** const route = get_page_route(self);
* 发送请求,应用维度上报 const routepath = get_route(self);
* @param {Object} options 页面信息
*/
sendReportRequest(options) {
this._navigationBarTitle.lt = '1';
this._navigationBarTitle.config = get_page_name(options.path);
let is_opt = options.query && JSON.stringify(options.query) !== '{}';
let query = is_opt ? '?' + JSON.stringify(options.query) : '';
Object.assign(this.statData, {
lt: '1',
url: (options.path + query) || '',
t: get_time(),
sc: get_scene(options.scene),
fvts: get_first_visit_time(),
lvts: get_last_visit_time(),
tvc: get_total_visit_count()
});
if (get_platform_name() === 'n') {
this.getProperty();
} else {
this.getNetworkInfo();
}
}
/** this._navigationBarTitle.config = get_page_name(routepath);
* 发送请求,页面维度上报 // 表示应用触发 ,页面切换不触发之后的逻辑
* @param {Object} opt if (this.__licationShow) {
*/ get_first_time();
sendPageRequest(opt) { // this._lastPageRoute = route
let { uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
url, this.__licationShow = false;
urlref, return
urlref_ts }
} = opt;
this._navigationBarTitle.lt = '11';
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '11',
ut: this.statData.ut,
url,
tt: this.statData.tt,
urlref,
urlref_ts,
ch: this.statData.ch,
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options);
}
/** get_last_time();
* 进入后台上报数据
* @param {Object} opt
* @param {Object} type
*/
sendHideRequest(opt, type) {
let {
urlref,
urlref_ts
} = opt;
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '3',
ut: this.statData.ut,
urlref,
urlref_ts,
ch: this.statData.ch,
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options, type);
}
/** const time = get_residence_time('page');
* 自定义事件上报 // 停留时间
*/ if (time.overtime) {
sendEventRequest({ let options = {
key = '', path: route,
value = '' scene: this.statData.sc,
} = {}) { };
// const route = this._lastPageRoute this.sendReportRequest(options);
const routepath = get_route(); }
this._navigationBarTitle.config = get_page_name(routepath); // 重置时间
this._navigationBarTitle.lt = '21'; get_first_time();
let options = { }
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '21',
ut: this.statData.ut,
url: routepath,
ch: this.statData.ch,
e_n: key,
e_v: typeof value === 'object' ? JSON.stringify(value) : value.toString(),
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options);
}
/** /**
* 获取wgt资源版本 * 离开页面触发
*/ */
getProperty() { pageHide(self) {
plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => { if (!this.__licationHide) {
this.statData.v = wgtinfo.version || ''; get_last_time();
this.getNetworkInfo(); const time = get_residence_time('page');
}); let route = get_page_route(self);
} let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
if (!lastPageRoute) {
lastPageRoute = route;
}
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
this.sendPageRequest({
url: route,
urlref: lastPageRoute,
urlref_ts: time.residenceTime,
});
// this._lastPageRoute = route
return
}
}
/**
* 获取网络信息
*/
getNetworkInfo() {
uni.getNetworkType({
success: (result) => {
this.statData.net = result.networkType;
this.getLocation();
},
});
}
/** /**
* 获取位置信息 * 发送请求,应用维度上报
*/ * @param {Object} options 页面信息
getLocation() { */
if (statConfig.getLocation) { sendReportRequest(options) {
uni.getLocation({ this._navigationBarTitle.lt = '1';
type: 'wgs84', this._navigationBarTitle.config = get_page_name(options.path);
geocode: true, let is_opt = options.query && JSON.stringify(options.query) !== '{}';
success: (result) => { let query = is_opt ? '?' + JSON.stringify(options.query) : '';
if (result.address) { Object.assign(this.statData, {
this.statData.cn = result.address.country; lt: '1',
this.statData.pn = result.address.province; url: (options.path + query) || '',
this.statData.ct = result.address.city; t: get_time(),
} sc: get_scene(options.scene),
fvts: get_first_visit_time(),
this.statData.lat = result.latitude; lvts: get_last_visit_time(),
this.statData.lng = result.longitude; tvc: get_total_visit_count()
this.request(this.statData); });
}, if (get_platform_name() === 'n') {
}); this.getProperty();
} else { } else {
this.statData.lat = 0; this.getNetworkInfo();
this.statData.lng = 0; }
this.request(this.statData); }
}
}
/** /**
* 发送请求 * 发送请求,页面维度上报
* @param {Object} data 上报数据 * @param {Object} opt
* @param {Object} type 类型 */
*/ sendPageRequest(opt) {
request(data, type) { let {
let time = get_time(); url,
const title = this._navigationBarTitle; urlref,
Object.assign(data, { urlref_ts
ttn: title.page, } = opt;
ttpj: title.config, this._navigationBarTitle.lt = '11';
ttc: title.report let options = {
}); ak: this.statData.ak,
uuid: this.statData.uuid,
let uniStatData = dbGet('__UNI__STAT__DATA') || {}; lt: '11',
if (!uniStatData[data.lt]) { ut: this.statData.ut,
uniStatData[data.lt] = []; url,
} tt: this.statData.tt,
// 加入队列 urlref,
uniStatData[data.lt].push(data); urlref_ts,
dbSet('__UNI__STAT__DATA', uniStatData); ch: this.statData.ch,
usv: this.statData.usv,
let page_residence_time = get_page_residence_time(); t: get_time(),
// 判断时候到达上报时间 ,默认 10 秒上报 p: this.statData.p,
if (page_residence_time < OPERATING_TIME && !type) return };
this.request(options);
// 时间超过,重新获取时间戳 }
set_page_residence_time();
const stat_data = handle_data(uniStatData);
let optionsData = {
usv: STAT_VERSION, //统计 SDK 版本号
t: time, //发送请求时的时间戮
requests: stat_data,
};
// 重置队列
dbRemove('__UNI__STAT__DATA');
if (data.ut === 'h5') {
this.imageRequest(optionsData);
return
}
// XXX 安卓需要延迟上报 ,否则会有未知错误,需要验证处理 /**
if (get_platform_name() === 'n' && this.statData.p === 'a') { * 进入后台上报数据
setTimeout(() => { * @param {Object} opt
this.sendRequest(optionsData); * @param {Object} type
}, 200); */
return sendHideRequest(opt, type) {
} let {
urlref,
urlref_ts
} = opt;
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '3',
ut: this.statData.ut,
urlref,
urlref_ts,
ch: this.statData.ch,
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options, type);
}
this.sendRequest(optionsData); /**
} * 自定义事件上报
getIsReportData(){ */
return is_report_data() sendEventRequest({
} key = '',
value = ''
} = {}) {
// const route = this._lastPageRoute
const routepath = get_route();
this._navigationBarTitle.config = get_page_name(routepath);
this._navigationBarTitle.lt = '21';
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '21',
ut: this.statData.ut,
url: routepath,
ch: this.statData.ch,
e_n: key,
e_v: typeof value === 'object' ? JSON.stringify(value) : value.toString(),
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options);
}
/** /**
* 数据上报 * 获取wgt资源版本
* @param {Object} optionsData 需要上报的数据 */
*/ getProperty() {
sendRequest(optionsData) { plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
this.getIsReportData().then(() => { this.statData.v = wgtinfo.version || '';
uni.request({ this.getNetworkInfo();
url: STAT_URL, });
method: 'POST', }
// header: {
// 'content-type': 'application/json' // 默认值
// },
data: optionsData,
success: () => {},
fail: (e) => {
if (++this._retry < 3) {
setTimeout(() => {
this.sendRequest(optionsData);
}, 1000);
}
},
});
});
}
/**
* h5 请求
*/
imageRequest(data) {
this.getIsReportData().then(() => {
let image = new Image();
let options = get_sgin(get_encodeURIComponent_options(data)).options;
image.src = STAT_H5_URL + '?' + options;
});
}
sendEvent(key, value) { /**
// 校验 type 参数 * 获取网络信息
if (calibration(key, value)) return */
getNetworkInfo() {
if (key === 'title') { uni.getNetworkType({
this._navigationBarTitle.report = value; success: (result) => {
return this.statData.net = result.networkType;
} this.getLocation();
this.sendEventRequest({ },
key, });
value: typeof value === 'object' ? JSON.stringify(value) : value, }
},
1 /**
); * 获取位置信息
} */
getLocation() {
if (stat_config.getLocation) {
uni.getLocation({
type: 'wgs84',
geocode: true,
success: (result) => {
if (result.address) {
this.statData.cn = result.address.country;
this.statData.pn = result.address.province;
this.statData.ct = result.address.city;
}
this.statData.lat = result.latitude;
this.statData.lng = result.longitude;
this.request(this.statData);
},
});
} else {
this.statData.lat = 0;
this.statData.lng = 0;
this.request(this.statData);
}
}
/**
* 发送请求
* @param {Object} data 上报数据
* @param {Object} type 类型
*/
request(data, type) {
let time = get_time();
const title = this._navigationBarTitle;
Object.assign(data, {
ttn: title.page,
ttpj: title.config,
ttc: title.report
});
let uniStatData = dbGet('__UNI__STAT__DATA') || {};
if (!uniStatData[data.lt]) {
uniStatData[data.lt] = [];
}
// 加入队列
uniStatData[data.lt].push(data);
dbSet('__UNI__STAT__DATA', uniStatData);
let page_residence_time = get_page_residence_time();
// 判断时候到达上报时间 ,默认 10 秒上报
if (page_residence_time < OPERATING_TIME && !type) return
// 时间超过,重新获取时间戳
set_page_residence_time();
const stat_data = handle_data(uniStatData);
let optionsData = {
usv: STAT_VERSION, //统计 SDK 版本号
t: time, //发送请求时的时间戮
requests: stat_data,
};
// 重置队列
dbRemove('__UNI__STAT__DATA');
// XXX 安卓需要延迟上报 ,否则会有未知错误,需要验证处理
if (get_platform_name() === 'n' && this.statData.p === 'a') {
setTimeout(() => {
this.sendRequest(optionsData);
}, 200);
return
}
this.sendRequest(optionsData);
}
getIsReportData(){
return is_report_data()
}
/**
* 数据上报
* @param {Object} optionsData 需要上报的数据
*/
sendRequest(optionsData) {
{
if (!uniCloud.config) {
console.error('当前尚未绑定服务空间.');
return
}
uniCloud.callFunction({
name: 'uni-stat-report',
data: optionsData,
success: (res) => {},
fail: (err) => {}
});
}
}
/**
* h5 请求
*/
imageRequest(data) {
this.getIsReportData().then(() => {
let image = new Image();
let options = get_sgin(get_encodeURIComponent_options(data)).options;
image.src = STAT_H5_URL + '?' + options;
});
}
sendEvent(key, value) {
// 校验 type 参数
if (calibration(key, value)) return
if (key === 'title') {
this._navigationBarTitle.report = value;
return
}
this.sendEventRequest({
key,
value: typeof value === 'object' ? JSON.stringify(value) : value,
},
1
);
}
} }
let vue = (Vue.default || Vue);
class Stat extends Report { class Stat extends Report {
static getInstance() { static getInstance() {
if (!vue.instance) { if (!uni.__stat_instance) {
vue.instance = new Stat(); uni.__stat_instance = new Stat();
} }
return vue.instance return uni.__stat_instance
} }
constructor() { constructor() {
super(); super();
this.instance = null;
} }
/** /**
...@@ -1076,7 +1092,7 @@ class Stat extends Report { ...@@ -1076,7 +1092,7 @@ class Stat extends Report {
*/ */
launch(options, self) { launch(options, self) {
// 初始化页面停留时间 start // 初始化页面停留时间 start
let residence_time = set_page_residence_time(); set_page_residence_time();
this.__licationShow = true; this.__licationShow = true;
this.sendReportRequest(options, true); this.sendReportRequest(options, true);
} }
...@@ -1098,9 +1114,21 @@ class Stat extends Report { ...@@ -1098,9 +1114,21 @@ class Stat extends Report {
if (get_page_types(self) === 'page') { if (get_page_types(self) === 'page') {
this.pageShow(self); this.pageShow(self);
} }
// #ifdef VUE3
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
if (get_page_types(self) === 'app') {
this.appShow();
}
}
// #endif
// #ifndef VUE3
if (get_page_types(self) === 'app') { if (get_page_types(self) === 'app') {
this.appShow(self); this.appShow();
} }
// #endif
} }
hide(self) { hide(self) {
...@@ -1108,9 +1136,21 @@ class Stat extends Report { ...@@ -1108,9 +1136,21 @@ class Stat extends Report {
if (get_page_types(self) === 'page') { if (get_page_types(self) === 'page') {
this.pageHide(self); this.pageHide(self);
} }
// #ifdef VUE3
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
if (get_page_types(self) === 'app') {
this.appHide();
}
}
// #endif
// #ifndef VUE3
if (get_page_types(self) === 'app') { if (get_page_types(self) === 'app') {
this.appHide(self); this.appHide();
} }
// #endif
} }
error(em) { error(em) {
...@@ -1143,9 +1183,10 @@ class Stat extends Report { ...@@ -1143,9 +1183,10 @@ class Stat extends Report {
}; };
this.request(options); this.request(options);
} }
} }
var Stat$1 = Stat;
const stat = Stat.getInstance(); const stat = Stat$1.getInstance();
// 用于判断是隐藏页面还是卸载页面 // 用于判断是隐藏页面还是卸载页面
let isHide = false; let isHide = false;
...@@ -1187,16 +1228,38 @@ const lifecycle = { ...@@ -1187,16 +1228,38 @@ const lifecycle = {
}; };
function main() { function main() {
if (process.env.NODE_ENV === 'development') { {
uni.report = function(type, options) {}; console.log('uni统计开启,version:2');
} else { }
console.log('统计已开启'); if (process.env.NODE_ENV === 'development') {
const Vue = require('vue'); uni.report = function(type, options) {};
(Vue.default || Vue).mixin(lifecycle); } else {
uni.report = function(type, options) { // #ifdef VUE3
stat.sendEvent(type, options); uni.onCreateVueApp((app) => {
}; app.mixin(lifecycle);
uni.report = function(type, options) {
stat.sendEvent(type, options);
};
});
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
uni.onAppHide(() => {
stat.appHide(get_page_vm());
});
uni.onAppShow(() => {
stat.appShow(get_page_vm());
});
}
// #endif
// #ifndef VUE3
const Vue = require('vue');
(Vue.default || Vue).mixin(lifecycle);
uni.report = function(type, options) {
stat.sendEvent(type, options);
};
// #endif
} }
} }
......
'use strict';
/**
* 获取系统信息
*/
const sys = uni.getSystemInfoSync();
// 访问开始即启动小程序,访问结束结分为:进入后台超过5min、在前台无任何操作超过30min、在新的来源打开小程序;
const STAT_VERSION = '0.0.1';
const STAT_URL = 'https://tongji.dcloud.io/uni/stat';
const STAT_H5_URL = 'https://tongji.dcloud.io/uni/stat.gif';
const PAGE_PVER_TIME = 1800; // 页面在前台无操作结束访问时间 单位s
const APP_PVER_TIME = 300; // 应用在后台结束访问时间 单位s
const OPERATING_TIME = 10; // 数据上报时间 单位s
const DIFF_TIME = 60 * 1000 * 60 * 24;
let statConfig = {
appid: ''
};
let titleJsons = {};
// #ifdef VUE3
statConfig.appid = process.env.UNI_APP_ID;
titleJsons = process.env.UNI_STAT_TITLE_JSON;
// #endif
// #ifndef VUE3
statConfig = require('uni-stat-config').default || require('uni-stat-config');
const pagesTitle = require('uni-pages?{"type":"style"}').default;
let pagesData = pagesTitle.pages;
for (let i in pagesData) {
const style = pagesData[i];
const titleText =
// MP
style.navigationBarTitleText ||
// ali
style.defaultTitle||
// H5 || App
style.navigationBar?.titleText ||
'';
if (titleText) {
titleJsons[i] = titleText;
}
}
// #endif
const UUID_KEY = '__DC_STAT_UUID';
const UUID_VALUE = '__DC_UUID_VALUE';
function getUuid() {
let uuid = '';
if (get_platform_name() === 'n') {
try {
uuid = plus.runtime.getDCloudId();
} catch (e) {
uuid = '';
}
return uuid
}
try {
uuid = uni.getStorageSync(UUID_KEY);
} catch (e) {
uuid = UUID_VALUE;
}
if (!uuid) {
uuid = Date.now() + '' + Math.floor(Math.random() * 1e7);
try {
uni.setStorageSync(UUID_KEY, uuid);
} catch (e) {
uni.setStorageSync(UUID_KEY, UUID_VALUE);
}
}
return uuid
}
/**
* 获取配置信息 如 appid
*/
const stat_config = statConfig;
const get_uuid = (statData) => {
// 有可能不存在 deviceId(一般不存在就是出bug了),就自己生成一个
return sys.deviceId || getUuid()
};
const get_sgin = (statData) => {
let arr = Object.keys(statData);
let sortArr = arr.sort();
let sgin = {};
let sginStr = '';
for (var i in sortArr) {
sgin[sortArr[i]] = statData[sortArr[i]];
sginStr += sortArr[i] + '=' + statData[sortArr[i]] + '&';
}
return {
sign: '',
options: sginStr.substr(0, sginStr.length - 1),
}
};
const get_encodeURIComponent_options = (statData) => {
let data = {};
for (let prop in statData) {
data[prop] = encodeURIComponent(statData[prop]);
}
return data
};
/**
* 获取当前平台
* 移动端 : 'n',
* h5 : 'h5',
* 微信 : 'wx',
* 阿里 : 'ali',
* 百度 : 'bd',
* 头条 : 'tt',
* qq : 'qq',
* 快应用 : 'qn',
* 快手 : 'ks',
* 飞书 : 'lark',
* 快应用 : 'qw',
* 钉钉 : 'dt'
*/
const get_platform_name = () => {
// 苹果审核代码中禁止出现 alipay 字样 ,需要特殊处理一下
const aliArr = ['y', 'a', 'p', 'mp-ali'];
const platformList = {
'app': 'n',
'app-plus': 'n',
h5: 'h5',
'mp-weixin': 'wx',
[aliArr.reverse().join('')]: 'ali',
'mp-baidu': 'bd',
'mp-toutiao': 'tt',
'mp-qq': 'qq',
'quickapp-native': 'qn',
'mp-kuaishou': 'ks',
'mp-lark': 'lark',
'quickapp-webview':'qw'
};
if(platformList[process.env.VUE_APP_PLATFORM] === 'ali'){
if(my&&my.env){
const clientName = my.env.clientName;
if(clientName === 'ap') return 'ali'
if(clientName === 'dingtalk') return 'dt'
// TODO 缺少 ali 下的其他平台
}
}
return platformList[process.env.VUE_APP_PLATFORM]
};
/**
* 获取小程序 appid
*/
const get_pack_name = () => {
let packName = '';
if (get_platform_name() === 'wx' || get_platform_name() === 'qq') {
// 兼容微信小程序低版本基础库
if (uni.canIUse('getAccountInfoSync')) {
packName = uni.getAccountInfoSync().miniProgram.appId || '';
}
}
if (get_platform_name() === 'n') ;
return packName
};
/**
* 应用版本
*/
const get_version = () => {
return get_platform_name() === 'n' ? plus.runtime.version : ''
};
/**
* 获取渠道
*/
const get_channel = () => {
const platformName = get_platform_name();
let channel = '';
if (platformName === 'n') {
channel = plus.runtime.channel;
}
return channel
};
/**
* 获取小程序场景值
* @param {Object} options 页面信息
*/
const get_scene = (options) => {
const platformName = get_platform_name();
let scene = '';
if (options) {
return options
}
if (platformName === 'wx') {
scene = uni.getLaunchOptionsSync().scene;
}
return scene
};
/**
* 获取拼接参数
*/
const get_splicing = (data) => {
let str = '';
for (var i in data) {
str += i + '=' + data[i] + '&';
}
return str.substr(0, str.length - 1)
};
/**
* 获取页面url,不包含参数
*/
const get_route = (pageVm) => {
let _self = pageVm || get_page_vm();
if (get_platform_name() === 'bd') {
let mp_route = _self.$mp && _self.$mp.page && _self.$mp.page.is;
let scope_route = _self.$scope && _self.$scope.is;
return mp_route || scope_route || ''
} else {
return _self.route || (_self.$scope && _self.$scope.route) || (_self.$mp && _self.$mp.page.route)
}
};
/**
* 获取页面url, 包含参数
*/
const get_page_route = (pageVm) => {
// 从 app 进入应用 ,没有 $page ,获取不到路由 ,需要获取页面 尝试从 getCurrentPages 获取也页面实例
// FIXME 尽量不使用 getCurrentPages ,大部分获取路由是从 onHide 获取 ,这时可以获取到,如果是 onload ,则可能获取不到,比如 百度
let page = pageVm.$page || (pageVm.$scope && pageVm.$scope.$page);
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
if (!page) return lastPageRoute || ''
// 如果找不到 fullPath 就取 route 的值
return page.fullPath === '/' ? page.route : (page.fullPath||page.route)
};
/**
* 获取页面实例
*/
const get_page_vm = () => {
let pages = getCurrentPages();
let $page = pages[pages.length - 1];
if (!$page) return null
return $page.$vm
};
/**
* 获取页面类型
*/
const get_page_types = (self) => {
// XXX 百度有问题 ,获取的都是 componet ,等待修复
if (self.mpType === 'page' || self.$mpType === 'page' || (self.$mp && self.$mp.mpType === 'page') || self
.$options.mpType === 'page') {
return 'page';
}
if (self.mpType === 'app' || self.$mpType === 'app' || (self.$mp && self.$mp.mpType === 'app') || self.$options
.mpType === 'app') {
return 'app'
}
return null;
};
/**
* 处理上报参数
* @param {Object} 需要处理的数据
*/
const handle_data = (statData) => {
let firstArr = [];
let contentArr = [];
let lastArr = [];
for (let i in statData) {
const rd = statData[i];
rd.forEach((elm) => {
const newData = get_splicing(elm);
if (i === 0) {
firstArr.push(newData);
} else if (i === 3) {
lastArr.push(newData);
} else {
contentArr.push(newData);
}
});
}
firstArr.push(...contentArr, ...lastArr);
// 参数需要处理成字符串,方便上传
return JSON.stringify(firstArr)
};
/**
* 自定义事件参数校验
*/
const calibration = (eventName, options) => {
// login 、 share 、pay_success 、pay_fail 、register 、title
if (!eventName) {
console.error(`uni.report Missing [eventName] parameter`);
return true
}
if (typeof eventName !== 'string') {
console.error(`uni.report [eventName] Parameter type error, it can only be of type String`);
return true
}
if (eventName.length > 255) {
console.error(`uni.report [eventName] Parameter length cannot be greater than 255`);
return true
}
if (typeof options !== 'string' && typeof options !== 'object') {
console.error('uni.report [options] Parameter type error, Only supports String or Object type');
return true
}
if (typeof options === 'string' && options.length > 255) {
console.error(`uni.report [options] Parameter length cannot be greater than 255`);
return true
}
if (eventName === 'title' && typeof options !== 'string') {
console.error(
`uni.report [eventName] When the parameter is title, the [options] parameter can only be of type String`
);
return true
}
};
const get_page_name = (routepath) => {
return (titleJsons && titleJsons[routepath]) || ''
};
const Report_Data_Time = 'Report_Data_Time';
const Report_Status = 'Report_Status';
const is_report_data = () => {
return new Promise((resolve, reject) => {
let start_time = '';
let end_time = new Date().getTime();
let diff_time = DIFF_TIME;
let report_status = 1;
try {
start_time = uni.getStorageSync(Report_Data_Time);
report_status = uni.getStorageSync(Report_Status);
} catch (e) {
start_time = '';
report_status = 1;
}
if (report_status === '') {
requestData(({ enable }) => {
uni.setStorageSync(Report_Data_Time, end_time);
uni.setStorageSync(Report_Status, enable);
if (enable === 1) {
resolve();
}
});
return
}
if (report_status === 1) {
resolve();
}
if (!start_time) {
uni.setStorageSync(Report_Data_Time, end_time);
start_time = end_time;
}
if (end_time - start_time > diff_time) {
requestData(({ enable }) => {
uni.setStorageSync(Report_Data_Time, end_time);
uni.setStorageSync(Report_Status, enable);
});
}
})
};
const requestData = (done) => {
const appid = process.env.UNI_APP_ID;
let formData = {
usv: STAT_VERSION,
conf: JSON.stringify({
ak: appid,
}),
};
uni.request({
url: STAT_URL,
method: 'GET',
data: formData,
success: (res) => {
const { data } = res;
if (data.ret === 0) {
typeof done === 'function' &&
done({
enable: data.enable,
});
}
},
fail: (e) => {
let report_status_code = 1;
try {
report_status_code = uni.getStorageSync(Report_Status);
} catch (e) {
report_status_code = 1;
}
if (report_status_code === '') {
report_status_code = 1;
}
typeof done === 'function' &&
done({
enable: report_status_code,
});
},
});
};
let data = uni.getStorageSync('$$STAT__DBDATA') || {};
const dbSet = (name, value) => {
if (!data) {
data = {};
}
data[name] = value;
uni.setStorageSync('$$STAT__DBDATA', data);
};
const dbGet = (name) => {
if (!data[name]) {
let dbdata = uni.getStorageSync('$$STAT__DBDATA');
if (!dbdata) {
dbdata = {};
}
if (!dbdata[name]) {
return undefined
}
data[name] = dbdata[name];
}
return data[name]
};
const dbRemove = (name) => {
if (data[name]) {
delete data[name];
uni.setStorageSync('$$STAT__DBDATA', data);
} else {
data = uni.getStorageSync('$$STAT__DBDATA');
if (data[name]) {
delete data[name];
uni.setStorageSync('$$STAT__DBDATA', data);
}
}
};
// 首次访问时间
const FIRST_VISIT_TIME_KEY = '__first__visit__time';
// 最后访问时间
const LAST_VISIT_TIME_KEY = '__last__visit__time';
/**
* 获取当前时间
*/
const get_time = () => {
return parseInt(new Date().getTime() / 1000)
};
/**
* 获取首次访问时间
*/
const get_first_visit_time = () => {
const timeStorge = dbGet(FIRST_VISIT_TIME_KEY);
let time = 0;
if (timeStorge) {
time = timeStorge;
} else {
time = get_time();
dbSet(FIRST_VISIT_TIME_KEY, time);
// 首次访问需要 将最后访问时间置 0
dbRemove(LAST_VISIT_TIME_KEY);
}
return time
};
/**
* 最后访问时间
*/
const get_last_visit_time = () => {
const timeStorge = dbGet(LAST_VISIT_TIME_KEY);
let time = 0;
if (timeStorge) {
time = timeStorge;
}
dbSet(LAST_VISIT_TIME_KEY, get_time());
return time
};
// 页面停留时间记录key
const PAGE_RESIDENCE_TIME = '__page__residence__time';
let First_Page_Residence_Time = 0;
let Last_Page_Residence_Time = 0;
/**
* 设置页面停留时间
*/
const set_page_residence_time = () => {
First_Page_Residence_Time = get_time();
dbSet(PAGE_RESIDENCE_TIME, First_Page_Residence_Time);
return First_Page_Residence_Time
};
/**
* 获取页面停留时间
*/
const get_page_residence_time = () => {
Last_Page_Residence_Time = get_time();
First_Page_Residence_Time = dbGet(PAGE_RESIDENCE_TIME);
return Last_Page_Residence_Time - First_Page_Residence_Time
};
/**
* 获取总访问次数
*/
const TOTAL_VISIT_COUNT = '__total__visit__count';
const get_total_visit_count = () => {
const timeStorge = dbGet(TOTAL_VISIT_COUNT);
let count = 1;
if (timeStorge) {
count = timeStorge;
count++;
}
dbSet(TOTAL_VISIT_COUNT, count);
return count
};
let Set__First__Time = 0;
let Set__Last__Time = 0;
/**
* 获取第一次时间
*/
const get_first_time = () => {
let time = new Date().getTime();
Set__First__Time = time;
Set__Last__Time = 0;
return time
};
/**
* 获取最后一次时间
*/
const get_last_time = () => {
let time = new Date().getTime();
Set__Last__Time = time;
return time
};
/**
* 获取页面 \ 应用停留时间
*/
const get_residence_time = (type) => {
let residenceTime = 0;
if (Set__First__Time !== 0) {
residenceTime = Set__Last__Time - Set__First__Time;
}
residenceTime = parseInt(residenceTime / 1000);
residenceTime = residenceTime < 1 ? 1 : residenceTime;
if (type === 'app') {
let overtime = residenceTime > APP_PVER_TIME ? true : false;
return {
residenceTime,
overtime,
}
}
if (type === 'page') {
let overtime = residenceTime > PAGE_PVER_TIME ? true : false;
return {
residenceTime,
overtime,
}
}
return {
residenceTime,
}
};
// 统计数据默认值
let statData = {
uuid: get_uuid(), // 设备标识
ut: get_platform_name(), // 平台类型
mpn: get_pack_name(), // 原生平台包名、小程序 appid
ak: stat_config.appid, // uni-app 应用 Appid
usv: STAT_VERSION, // 统计 sdk 版本
v: get_version(), // 应用版本,仅app
ch: get_channel(), // 渠道信息
cn: '', // 国家
pn: '', // 省份
ct: '', // 城市
t: get_time(), // 上报数据时的时间戳
tt: '',
p: sys.platform === 'android' ? 'a' : 'i', // 手机系统
brand: sys.brand || '', // 手机品牌
md: sys.model, // 手机型号
sv: sys.system.replace(/(Android|iOS)\s/, ''), // 手机系统版本
mpsdk: sys.SDKVersion || '', // x程序 sdk version
mpv: sys.version || '', // 小程序平台版本 ,如微信、支付宝
lang: sys.language, // 语言
pr: sys.pixelRatio, // pixelRatio 设备像素比
ww: sys.windowWidth, // windowWidth 可使用窗口宽度
wh: sys.windowHeight, // windowHeight 可使用窗口高度
sw: sys.screenWidth, // screenWidth 屏幕宽度
sh: sys.screenHeight, // screenHeight 屏幕高度
};
class Report {
constructor() {
// 页面实例
this.self = '';
// 进入应用标识
this.__licationShow = false;
// 离开应用标识
this.__licationHide = false;
// 统计默认值
this.statData = statData;
// 标题默认值
this._navigationBarTitle = {
config: '',
page: '',
report: '',
lt: '',
};
// 页面参数
this._query = {};
// 页面最后停留页面的 url
// this._lastPageRoute = ''
// 注册拦截器
let registerInterceptor = typeof uni.addInterceptor === 'function';
if (registerInterceptor) {
this.addInterceptorInit();
this.interceptLogin();
this.interceptShare(true);
this.interceptRequestPayment();
}
}
addInterceptorInit() {
let self = this;
uni.addInterceptor('setNavigationBarTitle', {
invoke(args) {
self._navigationBarTitle.page = args.title;
},
});
}
interceptLogin() {
let self = this;
uni.addInterceptor('login', {
complete() {
self._login();
},
});
}
interceptShare(type) {
let self = this;
if (!type) {
self._share();
return
}
uni.addInterceptor('share', {
success() {
self._share();
},
fail() {
self._share();
},
});
}
interceptRequestPayment() {
let self = this;
uni.addInterceptor('requestPayment', {
success() {
self._payment('pay_success');
},
fail() {
self._payment('pay_fail');
},
});
}
_login() {
this.sendEventRequest({
key: 'login',
},
0
);
}
_share() {
this.sendEventRequest({
key: 'share',
}, 0);
}
_payment(key) {
this.sendEventRequest({
key,
}, 0);
}
/**
* 进入应用触发
*/
applicationShow() {
// 通过 __licationHide 判断保证是进入后台后在次进入应用,避免重复上报数据
if (this.__licationHide) {
get_last_time();
const time = get_residence_time('app');
// 需要判断进入后台是否超过时限 ,默认是 30min ,是的话需要执行进入应用的上报
if (time.overtime) {
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
let options = {
path: lastPageRoute,
scene: this.statData.sc,
};
this.sendReportRequest(options);
}
// 状态重置
this.__licationHide = false;
}
}
/**
* 离开应用触发
* @param {Object} self
* @param {Object} type
*/
applicationHide(self, type) {
if(!self){
// 表示应用切换到后台 ,此时需要从页面栈获取页面实例
self = get_page_vm();
}
// 进入应用后台保存状态,方便进入前台后判断是否上报应用数据
this.__licationHide = true;
get_last_time();
const time = get_residence_time();
const route = get_page_route(self);
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
this.sendHideRequest({
urlref: route,
urlref_ts: time.residenceTime,
},
type
);
// 重置时间
get_first_time();
}
/**
* 进入页面触发
*/
pageShow(self) {
// 清空值 ,初始化 ,避免污染后面的上报数据
this._navigationBarTitle = {
config: '',
page: '',
report: '',
lt: '',
};
const route = get_page_route(self);
const routepath = get_route(self);
this._navigationBarTitle.config = get_page_name(routepath);
// 表示应用触发 ,页面切换不触发之后的逻辑
if (this.__licationShow) {
get_first_time();
// this._lastPageRoute = route
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
this.__licationShow = false;
return
}
get_last_time();
const time = get_residence_time('page');
// 停留时间
if (time.overtime) {
let options = {
path: route,
scene: this.statData.sc,
};
this.sendReportRequest(options);
}
// 重置时间
get_first_time();
}
/**
* 离开页面触发
*/
pageHide(self) {
if (!this.__licationHide) {
get_last_time();
const time = get_residence_time('page');
let route = get_page_route(self);
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
if (!lastPageRoute) {
lastPageRoute = route;
}
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
this.sendPageRequest({
url: route,
urlref: lastPageRoute,
urlref_ts: time.residenceTime,
});
// this._lastPageRoute = route
return
}
}
/**
* 发送请求,应用维度上报
* @param {Object} options 页面信息
*/
sendReportRequest(options) {
this._navigationBarTitle.lt = '1';
this._navigationBarTitle.config = get_page_name(options.path);
let is_opt = options.query && JSON.stringify(options.query) !== '{}';
let query = is_opt ? '?' + JSON.stringify(options.query) : '';
Object.assign(this.statData, {
lt: '1',
url: (options.path + query) || '',
t: get_time(),
sc: get_scene(options.scene),
fvts: get_first_visit_time(),
lvts: get_last_visit_time(),
tvc: get_total_visit_count()
});
if (get_platform_name() === 'n') {
this.getProperty();
} else {
this.getNetworkInfo();
}
}
/**
* 发送请求,页面维度上报
* @param {Object} opt
*/
sendPageRequest(opt) {
let {
url,
urlref,
urlref_ts
} = opt;
this._navigationBarTitle.lt = '11';
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '11',
ut: this.statData.ut,
url,
tt: this.statData.tt,
urlref,
urlref_ts,
ch: this.statData.ch,
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options);
}
/**
* 进入后台上报数据
* @param {Object} opt
* @param {Object} type
*/
sendHideRequest(opt, type) {
let {
urlref,
urlref_ts
} = opt;
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '3',
ut: this.statData.ut,
urlref,
urlref_ts,
ch: this.statData.ch,
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options, type);
}
/**
* 自定义事件上报
*/
sendEventRequest({
key = '',
value = ''
} = {}) {
// const route = this._lastPageRoute
const routepath = get_route();
this._navigationBarTitle.config = get_page_name(routepath);
this._navigationBarTitle.lt = '21';
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '21',
ut: this.statData.ut,
url: routepath,
ch: this.statData.ch,
e_n: key,
e_v: typeof value === 'object' ? JSON.stringify(value) : value.toString(),
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options);
}
/**
* 获取wgt资源版本
*/
getProperty() {
plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
this.statData.v = wgtinfo.version || '';
this.getNetworkInfo();
});
}
/**
* 获取网络信息
*/
getNetworkInfo() {
uni.getNetworkType({
success: (result) => {
this.statData.net = result.networkType;
this.getLocation();
},
});
}
/**
* 获取位置信息
*/
getLocation() {
if (stat_config.getLocation) {
uni.getLocation({
type: 'wgs84',
geocode: true,
success: (result) => {
if (result.address) {
this.statData.cn = result.address.country;
this.statData.pn = result.address.province;
this.statData.ct = result.address.city;
}
this.statData.lat = result.latitude;
this.statData.lng = result.longitude;
this.request(this.statData);
},
});
} else {
this.statData.lat = 0;
this.statData.lng = 0;
this.request(this.statData);
}
}
/**
* 发送请求
* @param {Object} data 上报数据
* @param {Object} type 类型
*/
request(data, type) {
let time = get_time();
const title = this._navigationBarTitle;
Object.assign(data, {
ttn: title.page,
ttpj: title.config,
ttc: title.report
});
let uniStatData = dbGet('__UNI__STAT__DATA') || {};
if (!uniStatData[data.lt]) {
uniStatData[data.lt] = [];
}
// 加入队列
uniStatData[data.lt].push(data);
dbSet('__UNI__STAT__DATA', uniStatData);
let page_residence_time = get_page_residence_time();
// 判断时候到达上报时间 ,默认 10 秒上报
if (page_residence_time < OPERATING_TIME && !type) return
// 时间超过,重新获取时间戳
set_page_residence_time();
const stat_data = handle_data(uniStatData);
let optionsData = {
usv: STAT_VERSION, //统计 SDK 版本号
t: time, //发送请求时的时间戮
requests: stat_data,
};
// 重置队列
dbRemove('__UNI__STAT__DATA');
{
if (data.ut === 'h5') {
this.imageRequest(optionsData);
return
}
}
// XXX 安卓需要延迟上报 ,否则会有未知错误,需要验证处理
if (get_platform_name() === 'n' && this.statData.p === 'a') {
setTimeout(() => {
this.sendRequest(optionsData);
}, 200);
return
}
this.sendRequest(optionsData);
}
getIsReportData(){
return is_report_data()
}
/**
* 数据上报
* @param {Object} optionsData 需要上报的数据
*/
sendRequest(optionsData) {
{
this.getIsReportData().then(() => {
uni.request({
url: STAT_URL,
method: 'POST',
data: optionsData,
success: () => {},
fail: (e) => {
if (++this._retry < 3) {
setTimeout(() => {
this.sendRequest(optionsData);
}, 1000);
}
},
});
});
}
}
/**
* h5 请求
*/
imageRequest(data) {
this.getIsReportData().then(() => {
let image = new Image();
let options = get_sgin(get_encodeURIComponent_options(data)).options;
image.src = STAT_H5_URL + '?' + options;
});
}
sendEvent(key, value) {
// 校验 type 参数
if (calibration(key, value)) return
if (key === 'title') {
this._navigationBarTitle.report = value;
return
}
this.sendEventRequest({
key,
value: typeof value === 'object' ? JSON.stringify(value) : value,
},
1
);
}
}
class Stat extends Report {
static getInstance() {
if (!uni.__stat_instance) {
uni.__stat_instance = new Stat();
}
return uni.__stat_instance
}
constructor() {
super();
}
/**
* 进入应用
* @param {Object} options 页面参数
* @param {Object} self 当前页面实例
*/
launch(options, self) {
// 初始化页面停留时间 start
set_page_residence_time();
this.__licationShow = true;
this.sendReportRequest(options, true);
}
load(options, self) {
this.self = self;
this._query = options;
}
appHide(self){
this.applicationHide(self, true);
}
appShow(self){
this.applicationShow(self);
}
show(self) {
this.self = self;
if (get_page_types(self) === 'page') {
this.pageShow(self);
}
// #ifdef VUE3
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
if (get_page_types(self) === 'app') {
this.appShow();
}
}
// #endif
// #ifndef VUE3
if (get_page_types(self) === 'app') {
this.appShow();
}
// #endif
}
hide(self) {
this.self = self;
if (get_page_types(self) === 'page') {
this.pageHide(self);
}
// #ifdef VUE3
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
if (get_page_types(self) === 'app') {
this.appHide();
}
}
// #endif
// #ifndef VUE3
if (get_page_types(self) === 'app') {
this.appHide();
}
// #endif
}
error(em) {
// 开发工具内不上报错误
if (this._platform === 'devtools') {
if (process.env.NODE_ENV === 'development') {
console.info('当前运行环境为开发者工具,不上报数据。');
return;
}
}
let emVal = '';
if (!em.message) {
emVal = JSON.stringify(em);
} else {
emVal = em.stack;
}
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '31',
ut: this.statData.ut,
ch: this.statData.ch,
mpsdk: this.statData.mpsdk,
mpv: this.statData.mpv,
v: this.statData.v,
em: emVal,
usv: this.statData.usv,
t: parseInt(new Date().getTime() / 1000),
p: this.statData.p,
};
this.request(options);
}
}
var Stat$1 = Stat;
const stat = Stat$1.getInstance();
// 用于判断是隐藏页面还是卸载页面
let isHide = false;
const lifecycle = {
onLaunch(options) {
// 进入应用上报数据
stat.launch(options, this);
},
onLoad(options) {
stat.load(options, this);
// 重写分享,获取分享上报事件
if (this.$scope && this.$scope.onShareAppMessage) {
let oldShareAppMessage = this.$scope.onShareAppMessage;
this.$scope.onShareAppMessage = function(options) {
stat.interceptShare(false);
return oldShareAppMessage.call(this, options)
};
}
},
onShow() {
isHide = false;
stat.show(this);
},
onHide() {
isHide = true;
stat.hide(this);
},
onUnload() {
if (isHide) {
isHide = false;
return
}
stat.hide(this);
},
onError(e) {
stat.error(e);
}
};
function main() {
{
console.log('uni统计开启,version:1');
}
if (process.env.NODE_ENV === 'development') {
uni.report = function(type, options) {};
} else {
// #ifdef VUE3
uni.onCreateVueApp((app) => {
app.mixin(lifecycle);
uni.report = function(type, options) {
stat.sendEvent(type, options);
};
});
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
uni.onAppHide(() => {
stat.appHide(get_page_vm());
});
uni.onAppShow(() => {
stat.appShow(get_page_vm());
});
}
// #endif
// #ifndef VUE3
const Vue = require('vue');
(Vue.default || Vue).mixin(lifecycle);
uni.report = function(type, options) {
stat.sendEvent(type, options);
};
// #endif
}
}
main();
/**
* 获取系统信息
*/
const sys = uni.getSystemInfoSync();
// 访问开始即启动小程序,访问结束结分为:进入后台超过5min、在前台无任何操作超过30min、在新的来源打开小程序;
const STAT_VERSION = '0.0.1';
const STAT_URL = 'https://tongji.dcloud.io/uni/stat';
const STAT_H5_URL = 'https://tongji.dcloud.io/uni/stat.gif';
const PAGE_PVER_TIME = 1800; // 页面在前台无操作结束访问时间 单位s
const APP_PVER_TIME = 300; // 应用在后台结束访问时间 单位s
const OPERATING_TIME = 10; // 数据上报时间 单位s
const DIFF_TIME = 60 * 1000 * 60 * 24;
let statConfig = {
appid: ''
};
let titleJsons = {};
// #ifdef VUE3
statConfig.appid = process.env.UNI_APP_ID;
titleJsons = process.env.UNI_STAT_TITLE_JSON;
// #endif
// #ifndef VUE3
statConfig = require('uni-stat-config').default || require('uni-stat-config');
const pagesTitle = require('uni-pages?{"type":"style"}').default;
let pagesData = pagesTitle.pages;
for (let i in pagesData) {
const style = pagesData[i];
const titleText =
// MP
style.navigationBarTitleText ||
// ali
style.defaultTitle||
// H5 || App
style.navigationBar?.titleText ||
'';
if (titleText) {
titleJsons[i] = titleText;
}
}
// #endif
const UUID_KEY = '__DC_STAT_UUID';
const UUID_VALUE = '__DC_UUID_VALUE';
function getUuid() {
let uuid = '';
if (get_platform_name() === 'n') {
try {
uuid = plus.runtime.getDCloudId();
} catch (e) {
uuid = '';
}
return uuid
}
try {
uuid = uni.getStorageSync(UUID_KEY);
} catch (e) {
uuid = UUID_VALUE;
}
if (!uuid) {
uuid = Date.now() + '' + Math.floor(Math.random() * 1e7);
try {
uni.setStorageSync(UUID_KEY, uuid);
} catch (e) {
uni.setStorageSync(UUID_KEY, UUID_VALUE);
}
}
return uuid
}
/**
* 获取配置信息 如 appid
*/
const stat_config = statConfig;
const get_uuid = (statData) => {
// 有可能不存在 deviceId(一般不存在就是出bug了),就自己生成一个
return sys.deviceId || getUuid()
};
const get_sgin = (statData) => {
let arr = Object.keys(statData);
let sortArr = arr.sort();
let sgin = {};
let sginStr = '';
for (var i in sortArr) {
sgin[sortArr[i]] = statData[sortArr[i]];
sginStr += sortArr[i] + '=' + statData[sortArr[i]] + '&';
}
return {
sign: '',
options: sginStr.substr(0, sginStr.length - 1),
}
};
const get_encodeURIComponent_options = (statData) => {
let data = {};
for (let prop in statData) {
data[prop] = encodeURIComponent(statData[prop]);
}
return data
};
/**
* 获取当前平台
* 移动端 : 'n',
* h5 : 'h5',
* 微信 : 'wx',
* 阿里 : 'ali',
* 百度 : 'bd',
* 头条 : 'tt',
* qq : 'qq',
* 快应用 : 'qn',
* 快手 : 'ks',
* 飞书 : 'lark',
* 快应用 : 'qw',
* 钉钉 : 'dt'
*/
const get_platform_name = () => {
// 苹果审核代码中禁止出现 alipay 字样 ,需要特殊处理一下
const aliArr = ['y', 'a', 'p', 'mp-ali'];
const platformList = {
'app': 'n',
'app-plus': 'n',
h5: 'h5',
'mp-weixin': 'wx',
[aliArr.reverse().join('')]: 'ali',
'mp-baidu': 'bd',
'mp-toutiao': 'tt',
'mp-qq': 'qq',
'quickapp-native': 'qn',
'mp-kuaishou': 'ks',
'mp-lark': 'lark',
'quickapp-webview':'qw'
};
if(platformList[process.env.VUE_APP_PLATFORM] === 'ali'){
if(my&&my.env){
const clientName = my.env.clientName;
if(clientName === 'ap') return 'ali'
if(clientName === 'dingtalk') return 'dt'
// TODO 缺少 ali 下的其他平台
}
}
return platformList[process.env.VUE_APP_PLATFORM]
};
/**
* 获取小程序 appid
*/
const get_pack_name = () => {
let packName = '';
if (get_platform_name() === 'wx' || get_platform_name() === 'qq') {
// 兼容微信小程序低版本基础库
if (uni.canIUse('getAccountInfoSync')) {
packName = uni.getAccountInfoSync().miniProgram.appId || '';
}
}
if (get_platform_name() === 'n') ;
return packName
};
/**
* 应用版本
*/
const get_version = () => {
return get_platform_name() === 'n' ? plus.runtime.version : ''
};
/**
* 获取渠道
*/
const get_channel = () => {
const platformName = get_platform_name();
let channel = '';
if (platformName === 'n') {
channel = plus.runtime.channel;
}
return channel
};
/**
* 获取小程序场景值
* @param {Object} options 页面信息
*/
const get_scene = (options) => {
const platformName = get_platform_name();
let scene = '';
if (options) {
return options
}
if (platformName === 'wx') {
scene = uni.getLaunchOptionsSync().scene;
}
return scene
};
/**
* 获取拼接参数
*/
const get_splicing = (data) => {
let str = '';
for (var i in data) {
str += i + '=' + data[i] + '&';
}
return str.substr(0, str.length - 1)
};
/**
* 获取页面url,不包含参数
*/
const get_route = (pageVm) => {
let _self = pageVm || get_page_vm();
if (get_platform_name() === 'bd') {
let mp_route = _self.$mp && _self.$mp.page && _self.$mp.page.is;
let scope_route = _self.$scope && _self.$scope.is;
return mp_route || scope_route || ''
} else {
return _self.route || (_self.$scope && _self.$scope.route) || (_self.$mp && _self.$mp.page.route)
}
};
/**
* 获取页面url, 包含参数
*/
const get_page_route = (pageVm) => {
// 从 app 进入应用 ,没有 $page ,获取不到路由 ,需要获取页面 尝试从 getCurrentPages 获取也页面实例
// FIXME 尽量不使用 getCurrentPages ,大部分获取路由是从 onHide 获取 ,这时可以获取到,如果是 onload ,则可能获取不到,比如 百度
let page = pageVm.$page || (pageVm.$scope && pageVm.$scope.$page);
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
if (!page) return lastPageRoute || ''
// 如果找不到 fullPath 就取 route 的值
return page.fullPath === '/' ? page.route : (page.fullPath||page.route)
};
/**
* 获取页面实例
*/
const get_page_vm = () => {
let pages = getCurrentPages();
let $page = pages[pages.length - 1];
if (!$page) return null
return $page.$vm
};
/**
* 获取页面类型
*/
const get_page_types = (self) => {
// XXX 百度有问题 ,获取的都是 componet ,等待修复
if (self.mpType === 'page' || self.$mpType === 'page' || (self.$mp && self.$mp.mpType === 'page') || self
.$options.mpType === 'page') {
return 'page';
}
if (self.mpType === 'app' || self.$mpType === 'app' || (self.$mp && self.$mp.mpType === 'app') || self.$options
.mpType === 'app') {
return 'app'
}
return null;
};
/**
* 处理上报参数
* @param {Object} 需要处理的数据
*/
const handle_data = (statData) => {
let firstArr = [];
let contentArr = [];
let lastArr = [];
for (let i in statData) {
const rd = statData[i];
rd.forEach((elm) => {
const newData = get_splicing(elm);
if (i === 0) {
firstArr.push(newData);
} else if (i === 3) {
lastArr.push(newData);
} else {
contentArr.push(newData);
}
});
}
firstArr.push(...contentArr, ...lastArr);
// 参数需要处理成字符串,方便上传
return JSON.stringify(firstArr)
};
/**
* 自定义事件参数校验
*/
const calibration = (eventName, options) => {
// login 、 share 、pay_success 、pay_fail 、register 、title
if (!eventName) {
console.error(`uni.report Missing [eventName] parameter`);
return true
}
if (typeof eventName !== 'string') {
console.error(`uni.report [eventName] Parameter type error, it can only be of type String`);
return true
}
if (eventName.length > 255) {
console.error(`uni.report [eventName] Parameter length cannot be greater than 255`);
return true
}
if (typeof options !== 'string' && typeof options !== 'object') {
console.error('uni.report [options] Parameter type error, Only supports String or Object type');
return true
}
if (typeof options === 'string' && options.length > 255) {
console.error(`uni.report [options] Parameter length cannot be greater than 255`);
return true
}
if (eventName === 'title' && typeof options !== 'string') {
console.error(
`uni.report [eventName] When the parameter is title, the [options] parameter can only be of type String`
);
return true
}
};
const get_page_name = (routepath) => {
return (titleJsons && titleJsons[routepath]) || ''
};
const Report_Data_Time = 'Report_Data_Time';
const Report_Status = 'Report_Status';
const is_report_data = () => {
return new Promise((resolve, reject) => {
let start_time = '';
let end_time = new Date().getTime();
let diff_time = DIFF_TIME;
let report_status = 1;
try {
start_time = uni.getStorageSync(Report_Data_Time);
report_status = uni.getStorageSync(Report_Status);
} catch (e) {
start_time = '';
report_status = 1;
}
if (report_status === '') {
requestData(({ enable }) => {
uni.setStorageSync(Report_Data_Time, end_time);
uni.setStorageSync(Report_Status, enable);
if (enable === 1) {
resolve();
}
});
return
}
if (report_status === 1) {
resolve();
}
if (!start_time) {
uni.setStorageSync(Report_Data_Time, end_time);
start_time = end_time;
}
if (end_time - start_time > diff_time) {
requestData(({ enable }) => {
uni.setStorageSync(Report_Data_Time, end_time);
uni.setStorageSync(Report_Status, enable);
});
}
})
};
const requestData = (done) => {
const appid = process.env.UNI_APP_ID;
let formData = {
usv: STAT_VERSION,
conf: JSON.stringify({
ak: appid,
}),
};
uni.request({
url: STAT_URL,
method: 'GET',
data: formData,
success: (res) => {
const { data } = res;
if (data.ret === 0) {
typeof done === 'function' &&
done({
enable: data.enable,
});
}
},
fail: (e) => {
let report_status_code = 1;
try {
report_status_code = uni.getStorageSync(Report_Status);
} catch (e) {
report_status_code = 1;
}
if (report_status_code === '') {
report_status_code = 1;
}
typeof done === 'function' &&
done({
enable: report_status_code,
});
},
});
};
let data = uni.getStorageSync('$$STAT__DBDATA') || {};
const dbSet = (name, value) => {
if (!data) {
data = {};
}
data[name] = value;
uni.setStorageSync('$$STAT__DBDATA', data);
};
const dbGet = (name) => {
if (!data[name]) {
let dbdata = uni.getStorageSync('$$STAT__DBDATA');
if (!dbdata) {
dbdata = {};
}
if (!dbdata[name]) {
return undefined
}
data[name] = dbdata[name];
}
return data[name]
};
const dbRemove = (name) => {
if (data[name]) {
delete data[name];
uni.setStorageSync('$$STAT__DBDATA', data);
} else {
data = uni.getStorageSync('$$STAT__DBDATA');
if (data[name]) {
delete data[name];
uni.setStorageSync('$$STAT__DBDATA', data);
}
}
};
// 首次访问时间
const FIRST_VISIT_TIME_KEY = '__first__visit__time';
// 最后访问时间
const LAST_VISIT_TIME_KEY = '__last__visit__time';
/**
* 获取当前时间
*/
const get_time = () => {
return parseInt(new Date().getTime() / 1000)
};
/**
* 获取首次访问时间
*/
const get_first_visit_time = () => {
const timeStorge = dbGet(FIRST_VISIT_TIME_KEY);
let time = 0;
if (timeStorge) {
time = timeStorge;
} else {
time = get_time();
dbSet(FIRST_VISIT_TIME_KEY, time);
// 首次访问需要 将最后访问时间置 0
dbRemove(LAST_VISIT_TIME_KEY);
}
return time
};
/**
* 最后访问时间
*/
const get_last_visit_time = () => {
const timeStorge = dbGet(LAST_VISIT_TIME_KEY);
let time = 0;
if (timeStorge) {
time = timeStorge;
}
dbSet(LAST_VISIT_TIME_KEY, get_time());
return time
};
// 页面停留时间记录key
const PAGE_RESIDENCE_TIME = '__page__residence__time';
let First_Page_Residence_Time = 0;
let Last_Page_Residence_Time = 0;
/**
* 设置页面停留时间
*/
const set_page_residence_time = () => {
First_Page_Residence_Time = get_time();
dbSet(PAGE_RESIDENCE_TIME, First_Page_Residence_Time);
return First_Page_Residence_Time
};
/**
* 获取页面停留时间
*/
const get_page_residence_time = () => {
Last_Page_Residence_Time = get_time();
First_Page_Residence_Time = dbGet(PAGE_RESIDENCE_TIME);
return Last_Page_Residence_Time - First_Page_Residence_Time
};
/**
* 获取总访问次数
*/
const TOTAL_VISIT_COUNT = '__total__visit__count';
const get_total_visit_count = () => {
const timeStorge = dbGet(TOTAL_VISIT_COUNT);
let count = 1;
if (timeStorge) {
count = timeStorge;
count++;
}
dbSet(TOTAL_VISIT_COUNT, count);
return count
};
let Set__First__Time = 0;
let Set__Last__Time = 0;
/**
* 获取第一次时间
*/
const get_first_time = () => {
let time = new Date().getTime();
Set__First__Time = time;
Set__Last__Time = 0;
return time
};
/**
* 获取最后一次时间
*/
const get_last_time = () => {
let time = new Date().getTime();
Set__Last__Time = time;
return time
};
/**
* 获取页面 \ 应用停留时间
*/
const get_residence_time = (type) => {
let residenceTime = 0;
if (Set__First__Time !== 0) {
residenceTime = Set__Last__Time - Set__First__Time;
}
residenceTime = parseInt(residenceTime / 1000);
residenceTime = residenceTime < 1 ? 1 : residenceTime;
if (type === 'app') {
let overtime = residenceTime > APP_PVER_TIME ? true : false;
return {
residenceTime,
overtime,
}
}
if (type === 'page') {
let overtime = residenceTime > PAGE_PVER_TIME ? true : false;
return {
residenceTime,
overtime,
}
}
return {
residenceTime,
}
};
// 统计数据默认值
let statData = {
uuid: get_uuid(), // 设备标识
ut: get_platform_name(), // 平台类型
mpn: get_pack_name(), // 原生平台包名、小程序 appid
ak: stat_config.appid, // uni-app 应用 Appid
usv: STAT_VERSION, // 统计 sdk 版本
v: get_version(), // 应用版本,仅app
ch: get_channel(), // 渠道信息
cn: '', // 国家
pn: '', // 省份
ct: '', // 城市
t: get_time(), // 上报数据时的时间戳
tt: '',
p: sys.platform === 'android' ? 'a' : 'i', // 手机系统
brand: sys.brand || '', // 手机品牌
md: sys.model, // 手机型号
sv: sys.system.replace(/(Android|iOS)\s/, ''), // 手机系统版本
mpsdk: sys.SDKVersion || '', // x程序 sdk version
mpv: sys.version || '', // 小程序平台版本 ,如微信、支付宝
lang: sys.language, // 语言
pr: sys.pixelRatio, // pixelRatio 设备像素比
ww: sys.windowWidth, // windowWidth 可使用窗口宽度
wh: sys.windowHeight, // windowHeight 可使用窗口高度
sw: sys.screenWidth, // screenWidth 屏幕宽度
sh: sys.screenHeight, // screenHeight 屏幕高度
};
class Report {
constructor() {
// 页面实例
this.self = '';
// 进入应用标识
this.__licationShow = false;
// 离开应用标识
this.__licationHide = false;
// 统计默认值
this.statData = statData;
// 标题默认值
this._navigationBarTitle = {
config: '',
page: '',
report: '',
lt: '',
};
// 页面参数
this._query = {};
// 页面最后停留页面的 url
// this._lastPageRoute = ''
// 注册拦截器
let registerInterceptor = typeof uni.addInterceptor === 'function';
if (registerInterceptor) {
this.addInterceptorInit();
this.interceptLogin();
this.interceptShare(true);
this.interceptRequestPayment();
}
}
addInterceptorInit() {
let self = this;
uni.addInterceptor('setNavigationBarTitle', {
invoke(args) {
self._navigationBarTitle.page = args.title;
},
});
}
interceptLogin() {
let self = this;
uni.addInterceptor('login', {
complete() {
self._login();
},
});
}
interceptShare(type) {
let self = this;
if (!type) {
self._share();
return
}
uni.addInterceptor('share', {
success() {
self._share();
},
fail() {
self._share();
},
});
}
interceptRequestPayment() {
let self = this;
uni.addInterceptor('requestPayment', {
success() {
self._payment('pay_success');
},
fail() {
self._payment('pay_fail');
},
});
}
_login() {
this.sendEventRequest({
key: 'login',
},
0
);
}
_share() {
this.sendEventRequest({
key: 'share',
}, 0);
}
_payment(key) {
this.sendEventRequest({
key,
}, 0);
}
/**
* 进入应用触发
*/
applicationShow() {
// 通过 __licationHide 判断保证是进入后台后在次进入应用,避免重复上报数据
if (this.__licationHide) {
get_last_time();
const time = get_residence_time('app');
// 需要判断进入后台是否超过时限 ,默认是 30min ,是的话需要执行进入应用的上报
if (time.overtime) {
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
let options = {
path: lastPageRoute,
scene: this.statData.sc,
};
this.sendReportRequest(options);
}
// 状态重置
this.__licationHide = false;
}
}
/**
* 离开应用触发
* @param {Object} self
* @param {Object} type
*/
applicationHide(self, type) {
if(!self){
// 表示应用切换到后台 ,此时需要从页面栈获取页面实例
self = get_page_vm();
}
// 进入应用后台保存状态,方便进入前台后判断是否上报应用数据
this.__licationHide = true;
get_last_time();
const time = get_residence_time();
const route = get_page_route(self);
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
this.sendHideRequest({
urlref: route,
urlref_ts: time.residenceTime,
},
type
);
// 重置时间
get_first_time();
}
/**
* 进入页面触发
*/
pageShow(self) {
// 清空值 ,初始化 ,避免污染后面的上报数据
this._navigationBarTitle = {
config: '',
page: '',
report: '',
lt: '',
};
const route = get_page_route(self);
const routepath = get_route(self);
this._navigationBarTitle.config = get_page_name(routepath);
// 表示应用触发 ,页面切换不触发之后的逻辑
if (this.__licationShow) {
get_first_time();
// this._lastPageRoute = route
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
this.__licationShow = false;
return
}
get_last_time();
const time = get_residence_time('page');
// 停留时间
if (time.overtime) {
let options = {
path: route,
scene: this.statData.sc,
};
this.sendReportRequest(options);
}
// 重置时间
get_first_time();
}
/**
* 离开页面触发
*/
pageHide(self) {
if (!this.__licationHide) {
get_last_time();
const time = get_residence_time('page');
let route = get_page_route(self);
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
if (!lastPageRoute) {
lastPageRoute = route;
}
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route);
this.sendPageRequest({
url: route,
urlref: lastPageRoute,
urlref_ts: time.residenceTime,
});
// this._lastPageRoute = route
return
}
}
/**
* 发送请求,应用维度上报
* @param {Object} options 页面信息
*/
sendReportRequest(options) {
this._navigationBarTitle.lt = '1';
this._navigationBarTitle.config = get_page_name(options.path);
let is_opt = options.query && JSON.stringify(options.query) !== '{}';
let query = is_opt ? '?' + JSON.stringify(options.query) : '';
Object.assign(this.statData, {
lt: '1',
url: (options.path + query) || '',
t: get_time(),
sc: get_scene(options.scene),
fvts: get_first_visit_time(),
lvts: get_last_visit_time(),
tvc: get_total_visit_count()
});
if (get_platform_name() === 'n') {
this.getProperty();
} else {
this.getNetworkInfo();
}
}
/**
* 发送请求,页面维度上报
* @param {Object} opt
*/
sendPageRequest(opt) {
let {
url,
urlref,
urlref_ts
} = opt;
this._navigationBarTitle.lt = '11';
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '11',
ut: this.statData.ut,
url,
tt: this.statData.tt,
urlref,
urlref_ts,
ch: this.statData.ch,
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options);
}
/**
* 进入后台上报数据
* @param {Object} opt
* @param {Object} type
*/
sendHideRequest(opt, type) {
let {
urlref,
urlref_ts
} = opt;
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '3',
ut: this.statData.ut,
urlref,
urlref_ts,
ch: this.statData.ch,
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options, type);
}
/**
* 自定义事件上报
*/
sendEventRequest({
key = '',
value = ''
} = {}) {
// const route = this._lastPageRoute
const routepath = get_route();
this._navigationBarTitle.config = get_page_name(routepath);
this._navigationBarTitle.lt = '21';
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '21',
ut: this.statData.ut,
url: routepath,
ch: this.statData.ch,
e_n: key,
e_v: typeof value === 'object' ? JSON.stringify(value) : value.toString(),
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
};
this.request(options);
}
/**
* 获取wgt资源版本
*/
getProperty() {
plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
this.statData.v = wgtinfo.version || '';
this.getNetworkInfo();
});
}
/**
* 获取网络信息
*/
getNetworkInfo() {
uni.getNetworkType({
success: (result) => {
this.statData.net = result.networkType;
this.getLocation();
},
});
}
/**
* 获取位置信息
*/
getLocation() {
if (stat_config.getLocation) {
uni.getLocation({
type: 'wgs84',
geocode: true,
success: (result) => {
if (result.address) {
this.statData.cn = result.address.country;
this.statData.pn = result.address.province;
this.statData.ct = result.address.city;
}
this.statData.lat = result.latitude;
this.statData.lng = result.longitude;
this.request(this.statData);
},
});
} else {
this.statData.lat = 0;
this.statData.lng = 0;
this.request(this.statData);
}
}
/**
* 发送请求
* @param {Object} data 上报数据
* @param {Object} type 类型
*/
request(data, type) {
let time = get_time();
const title = this._navigationBarTitle;
Object.assign(data, {
ttn: title.page,
ttpj: title.config,
ttc: title.report
});
let uniStatData = dbGet('__UNI__STAT__DATA') || {};
if (!uniStatData[data.lt]) {
uniStatData[data.lt] = [];
}
// 加入队列
uniStatData[data.lt].push(data);
dbSet('__UNI__STAT__DATA', uniStatData);
let page_residence_time = get_page_residence_time();
// 判断时候到达上报时间 ,默认 10 秒上报
if (page_residence_time < OPERATING_TIME && !type) return
// 时间超过,重新获取时间戳
set_page_residence_time();
const stat_data = handle_data(uniStatData);
let optionsData = {
usv: STAT_VERSION, //统计 SDK 版本号
t: time, //发送请求时的时间戮
requests: stat_data,
};
// 重置队列
dbRemove('__UNI__STAT__DATA');
{
if (data.ut === 'h5') {
this.imageRequest(optionsData);
return
}
}
// XXX 安卓需要延迟上报 ,否则会有未知错误,需要验证处理
if (get_platform_name() === 'n' && this.statData.p === 'a') {
setTimeout(() => {
this.sendRequest(optionsData);
}, 200);
return
}
this.sendRequest(optionsData);
}
getIsReportData(){
return is_report_data()
}
/**
* 数据上报
* @param {Object} optionsData 需要上报的数据
*/
sendRequest(optionsData) {
{
this.getIsReportData().then(() => {
uni.request({
url: STAT_URL,
method: 'POST',
data: optionsData,
success: () => {},
fail: (e) => {
if (++this._retry < 3) {
setTimeout(() => {
this.sendRequest(optionsData);
}, 1000);
}
},
});
});
}
}
/**
* h5 请求
*/
imageRequest(data) {
this.getIsReportData().then(() => {
let image = new Image();
let options = get_sgin(get_encodeURIComponent_options(data)).options;
image.src = STAT_H5_URL + '?' + options;
});
}
sendEvent(key, value) {
// 校验 type 参数
if (calibration(key, value)) return
if (key === 'title') {
this._navigationBarTitle.report = value;
return
}
this.sendEventRequest({
key,
value: typeof value === 'object' ? JSON.stringify(value) : value,
},
1
);
}
}
class Stat extends Report {
static getInstance() {
if (!uni.__stat_instance) {
uni.__stat_instance = new Stat();
}
return uni.__stat_instance
}
constructor() {
super();
}
/**
* 进入应用
* @param {Object} options 页面参数
* @param {Object} self 当前页面实例
*/
launch(options, self) {
// 初始化页面停留时间 start
set_page_residence_time();
this.__licationShow = true;
this.sendReportRequest(options, true);
}
load(options, self) {
this.self = self;
this._query = options;
}
appHide(self){
this.applicationHide(self, true);
}
appShow(self){
this.applicationShow(self);
}
show(self) {
this.self = self;
if (get_page_types(self) === 'page') {
this.pageShow(self);
}
// #ifdef VUE3
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
if (get_page_types(self) === 'app') {
this.appShow();
}
}
// #endif
// #ifndef VUE3
if (get_page_types(self) === 'app') {
this.appShow();
}
// #endif
}
hide(self) {
this.self = self;
if (get_page_types(self) === 'page') {
this.pageHide(self);
}
// #ifdef VUE3
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
if (get_page_types(self) === 'app') {
this.appHide();
}
}
// #endif
// #ifndef VUE3
if (get_page_types(self) === 'app') {
this.appHide();
}
// #endif
}
error(em) {
// 开发工具内不上报错误
if (this._platform === 'devtools') {
if (process.env.NODE_ENV === 'development') {
console.info('当前运行环境为开发者工具,不上报数据。');
return;
}
}
let emVal = '';
if (!em.message) {
emVal = JSON.stringify(em);
} else {
emVal = em.stack;
}
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '31',
ut: this.statData.ut,
ch: this.statData.ch,
mpsdk: this.statData.mpsdk,
mpv: this.statData.mpv,
v: this.statData.v,
em: emVal,
usv: this.statData.usv,
t: parseInt(new Date().getTime() / 1000),
p: this.statData.p,
};
this.request(options);
}
}
var Stat$1 = Stat;
const stat = Stat$1.getInstance();
// 用于判断是隐藏页面还是卸载页面
let isHide = false;
const lifecycle = {
onLaunch(options) {
// 进入应用上报数据
stat.launch(options, this);
},
onLoad(options) {
stat.load(options, this);
// 重写分享,获取分享上报事件
if (this.$scope && this.$scope.onShareAppMessage) {
let oldShareAppMessage = this.$scope.onShareAppMessage;
this.$scope.onShareAppMessage = function(options) {
stat.interceptShare(false);
return oldShareAppMessage.call(this, options)
};
}
},
onShow() {
isHide = false;
stat.show(this);
},
onHide() {
isHide = true;
stat.hide(this);
},
onUnload() {
if (isHide) {
isHide = false;
return
}
stat.hide(this);
},
onError(e) {
stat.error(e);
}
};
function main() {
{
console.log('uni统计开启,version:1');
}
if (process.env.NODE_ENV === 'development') {
uni.report = function(type, options) {};
} else {
// #ifdef VUE3
uni.onCreateVueApp((app) => {
app.mixin(lifecycle);
uni.report = function(type, options) {
stat.sendEvent(type, options);
};
});
if (get_platform_name() !== 'h5' && get_platform_name() !== 'n') {
uni.onAppHide(() => {
stat.appHide(get_page_vm());
});
uni.onAppShow(() => {
stat.appShow(get_page_vm());
});
}
// #endif
// #ifndef VUE3
const Vue = require('vue');
(Vue.default || Vue).mixin(lifecycle);
uni.report = function(type, options) {
stat.sendEvent(type, options);
};
// #endif
}
}
main();
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"name": "@dcloudio/uni-stat", "name": "@dcloudio/uni-stat",
"version": "2.0.1-alpha-34420220402001", "version": "2.0.1-alpha-34420220402001",
"description": "", "description": "",
"main": "dist/index.js", "main": "dist/uni-stat.es.js",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/dcloudio/uni-app.git", "url": "git+https://github.com/dcloudio/uni-app.git",
......
// 访问开始即启动小程序,访问结束结分为:进入后台超过5min、在前台无任何操作超过30min、在新的来源打开小程序;
export const STAT_VERSION = '0.0.1'
export const STAT_URL = 'https://tongji.dcloud.io/uni/stat'
export const STAT_H5_URL = 'https://tongji.dcloud.io/uni/stat.gif'
export const STAT_KEY = 'qkTHEIegZGcL5iy3'
export const PAGE_PVER_TIME = 1800 // 页面在前台无操作结束访问时间 单位s
export const APP_PVER_TIME = 300 // 应用在后台结束访问时间 单位s
export const OPERATING_TIME = 10 // 数据上报时间 单位s
export const DIFF_TIME = 60 * 1000 * 60 * 24
export const DEBUG = true
let statConfig = require('uni-stat-config').default || require('uni-stat-config');
import {
get_time,
set_page_residence_time,
get_first_visit_time,
get_last_visit_time,
get_total_visit_count,
get_page_residence_time,
get_first_time,
get_last_time,
get_residence_time
} from '../utils/pageTime.js'
import {
get_uuid,
get_platform_name,
get_pack_name,
get_scene,
get_version,
get_channel,
get_splicing,
get_page_route,
get_route,
handle_data,
calibration,
get_page_name,
get_sgin,
get_encodeURIComponent_options,
is_report_data,
} from '../utils/pageInfo.js'
import {
sys
} from '../utils/util.js'
import {
STAT_VERSION,
OPERATING_TIME,
STAT_URL,
STAT_H5_URL,
DEBUG
} from '../config.js';
import {
dbSet,
dbGet,
dbRemove
} from '../utils/db.js'
// 统计数据默认值
let statData = {
uuid: get_uuid(), // 设备标识
ut: get_platform_name(), // 平台类型
mpn: get_pack_name(), // 原生平台包名、小程序 appid
ak: statConfig.appid, // uni-app 应用 Appid
usv: STAT_VERSION, // 统计 sdk 版本
v: get_version(), // 应用版本,仅app
ch: get_channel(), // 渠道信息
cn: '', // 国家
pn: '', // 省份
ct: '', // 城市
t: get_time(), // 上报数据时的时间戳
tt: '',
p: sys.platform === 'android' ? 'a' : 'i', // 手机系统
brand: sys.brand || '', // 手机品牌
md: sys.model, // 手机型号
sv: sys.system.replace(/(Android|iOS)\s/, ''), // 手机系统版本
mpsdk: sys.SDKVersion || '', // x程序 sdk version
mpv: sys.version || '', // 小程序平台版本 ,如微信、支付宝
lang: sys.language, // 语言
pr: sys.pixelRatio, // pixelRatio 设备像素比
ww: sys.windowWidth, // windowWidth 可使用窗口宽度
wh: sys.windowHeight, // windowHeight 可使用窗口高度
sw: sys.screenWidth, // screenWidth 屏幕宽度
sh: sys.screenHeight, // screenHeight 屏幕高度
}
export default class Report {
constructor() {
// 页面实例
this.self = ''
// 进入应用标识
this.__licationShow = false
// 离开应用标识
this.__licationHide = false
// 统计默认值
this.statData = statData
// 标题默认值
this._navigationBarTitle = {
config: '',
page: '',
report: '',
lt: '',
}
// 页面参数
this._query = {}
// 页面最后停留页面的 url
// this._lastPageRoute = ''
// 注册拦截器
let registerInterceptor = typeof uni.addInterceptor === 'function'
if (registerInterceptor) {
this.addInterceptorInit()
this.interceptLogin()
this.interceptShare(true)
this.interceptRequestPayment()
}
}
addInterceptorInit() {
let self = this
uni.addInterceptor('setNavigationBarTitle', {
invoke(args) {
self._navigationBarTitle.page = args.title
},
})
}
interceptLogin() {
let self = this
uni.addInterceptor('login', {
complete() {
self._login()
},
})
}
interceptShare(type) {
let self = this
if (!type) {
self._share()
return
}
uni.addInterceptor('share', {
success() {
self._share()
},
fail() {
self._share()
},
})
}
interceptRequestPayment() {
let self = this
uni.addInterceptor('requestPayment', {
success() {
self._payment('pay_success')
},
fail() {
self._payment('pay_fail')
},
})
}
_login() {
this.sendEventRequest({
key: 'login',
},
0
)
}
_share() {
this.sendEventRequest({
key: 'share',
}, 0)
}
_payment(key) {
this.sendEventRequest({
key,
}, 0)
}
/**
* 进入应用触发
*/
applicationShow() {
// 通过 __licationHide 判断保证是进入后台后在次进入应用,避免重复上报数据
if (this.__licationHide) {
get_last_time()
const time = get_residence_time('app')
// 需要判断进入后台是否超过时限 ,默认是 30min ,是的话需要执行进入应用的上报
if (time.overtime) {
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE')
let options = {
path: lastPageRoute,
scene: this.statData.sc,
}
this.sendReportRequest(options)
}
// 状态重置
this.__licationHide = false
}
}
/**
* 离开应用触发
* @param {Object} self
* @param {Object} type
*/
applicationHide(self, type) {
// 进入应用后台保存状态,方便进入前台后判断是否上报应用数据
this.__licationHide = true
get_last_time()
const time = get_residence_time()
const route = get_page_route(self)
// this._lastPageRoute = route
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route)
this.sendHideRequest({
urlref: route,
urlref_ts: time.residenceTime,
},
type
)
// 重置时间
get_first_time()
}
/**
* 进入页面触发
*/
pageShow(self) {
// 清空值 ,初始化 ,避免污染后面的上报数据
this._navigationBarTitle = {
config: '',
page: '',
report: '',
lt: '',
}
const route = get_page_route(self)
const routepath = get_route(self)
this._navigationBarTitle.config = get_page_name(routepath)
// 表示应用触发 ,页面切换不触发之后的逻辑
if (this.__licationShow) {
get_first_time()
// this._lastPageRoute = route
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route)
this.__licationShow = false
return
}
get_last_time()
const time = get_residence_time('page')
// 停留时间
if (time.overtime) {
let options = {
path: route,
scene: this.statData.sc,
}
this.sendReportRequest(options)
}
// 重置时间
get_first_time()
}
/**
* 离开页面触发
*/
pageHide(self) {
if (!this.__licationHide) {
get_last_time()
const time = get_residence_time('page')
let route = get_page_route(self)
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE')
if (!lastPageRoute) {
lastPageRoute = route
}
uni.setStorageSync('_STAT_LAST_PAGE_ROUTE', route)
this.sendPageRequest({
url: route,
urlref: lastPageRoute,
urlref_ts: time.residenceTime,
})
// this._lastPageRoute = route
return
}
}
/**
* 发送请求,应用维度上报
* @param {Object} options 页面信息
*/
sendReportRequest(options) {
this._navigationBarTitle.lt = '1'
this._navigationBarTitle.config = get_page_name(options.path)
let is_opt = options.query && JSON.stringify(options.query) !== '{}'
let query = is_opt ? '?' + JSON.stringify(options.query) : ''
Object.assign(this.statData, {
lt: '1',
url: (options.path + query) || '',
t: get_time(),
sc: get_scene(options.scene),
fvts: get_first_visit_time(),
lvts: get_last_visit_time(),
tvc: get_total_visit_count()
})
if (get_platform_name() === 'n') {
this.getProperty()
} else {
this.getNetworkInfo()
}
}
/**
* 发送请求,页面维度上报
* @param {Object} opt
*/
sendPageRequest(opt) {
let {
url,
urlref,
urlref_ts
} = opt
this._navigationBarTitle.lt = '11'
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '11',
ut: this.statData.ut,
url,
tt: this.statData.tt,
urlref,
urlref_ts,
ch: this.statData.ch,
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
}
this.request(options)
}
/**
* 进入后台上报数据
* @param {Object} opt
* @param {Object} type
*/
sendHideRequest(opt, type) {
let {
urlref,
urlref_ts
} = opt
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '3',
ut: this.statData.ut,
urlref,
urlref_ts,
ch: this.statData.ch,
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
}
this.request(options, type)
}
/**
* 自定义事件上报
*/
sendEventRequest({
key = '',
value = ''
} = {}) {
// const route = this._lastPageRoute
const routepath = get_route()
this._navigationBarTitle.config = get_page_name(routepath)
this._navigationBarTitle.lt = '21'
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '21',
ut: this.statData.ut,
url: routepath,
ch: this.statData.ch,
e_n: key,
e_v: typeof value === 'object' ? JSON.stringify(value) : value.toString(),
usv: this.statData.usv,
t: get_time(),
p: this.statData.p,
}
this.request(options)
}
/**
* 获取wgt资源版本
*/
getProperty() {
plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
this.statData.v = wgtinfo.version || ''
this.getNetworkInfo()
})
}
/**
* 获取网络信息
*/
getNetworkInfo() {
uni.getNetworkType({
success: (result) => {
this.statData.net = result.networkType
this.getLocation()
},
})
}
/**
* 获取位置信息
*/
getLocation() {
if (statConfig.getLocation) {
uni.getLocation({
type: 'wgs84',
geocode: true,
success: (result) => {
if (result.address) {
this.statData.cn = result.address.country
this.statData.pn = result.address.province
this.statData.ct = result.address.city
}
this.statData.lat = result.latitude
this.statData.lng = result.longitude
this.request(this.statData)
},
})
} else {
this.statData.lat = 0
this.statData.lng = 0
this.request(this.statData)
}
}
/**
* 发送请求
* @param {Object} data 上报数据
* @param {Object} type 类型
*/
request(data, type) {
let time = get_time()
const title = this._navigationBarTitle
Object.assign(data, {
ttn: title.page,
ttpj: title.config,
ttc: title.report
})
let uniStatData = dbGet('__UNI__STAT__DATA') || {}
if (!uniStatData[data.lt]) {
uniStatData[data.lt] = []
}
// 加入队列
uniStatData[data.lt].push(data)
dbSet('__UNI__STAT__DATA', uniStatData)
let page_residence_time = get_page_residence_time()
// 判断时候到达上报时间 ,默认 10 秒上报
if (page_residence_time < OPERATING_TIME && !type) return
// 时间超过,重新获取时间戳
set_page_residence_time()
const stat_data = handle_data(uniStatData)
let optionsData = {
usv: STAT_VERSION, //统计 SDK 版本号
t: time, //发送请求时的时间戮
requests: stat_data,
}
// 重置队列
dbRemove('__UNI__STAT__DATA')
if (data.ut === 'h5') {
this.imageRequest(optionsData)
return
}
// XXX 安卓需要延迟上报 ,否则会有未知错误,需要验证处理
if (get_platform_name() === 'n' && this.statData.p === 'a') {
setTimeout(() => {
this.sendRequest(optionsData)
}, 200)
return
}
this.sendRequest(optionsData)
}
getIsReportData(){
return is_report_data()
}
/**
* 数据上报
* @param {Object} optionsData 需要上报的数据
*/
sendRequest(optionsData) {
this.getIsReportData().then(() => {
uni.request({
url: STAT_URL,
method: 'POST',
// header: {
// 'content-type': 'application/json' // 默认值
// },
data: optionsData,
success: () => {},
fail: (e) => {
if (++this._retry < 3) {
setTimeout(() => {
this.sendRequest(optionsData)
}, 1000)
}
},
})
})
}
/**
* h5 请求
*/
imageRequest(data) {
this.getIsReportData().then(() => {
let image = new Image()
let options = get_sgin(get_encodeURIComponent_options(data)).options
image.src = STAT_H5_URL + '?' + options
})
}
sendEvent(key, value) {
// 校验 type 参数
if (calibration(key, value)) return
if (key === 'title') {
this._navigationBarTitle.report = value
return
}
this.sendEventRequest({
key,
value: typeof value === 'object' ? JSON.stringify(value) : value,
},
1
)
}
}
import Report from './report.js'
import Vue from 'vue'
let vue = (Vue.default || Vue)
import {
set_page_residence_time
} from '../utils/pageTime.js'
import {
get_page_types,
get_platform_name
} from '../utils/pageInfo.js'
class Stat extends Report {
static getInstance() {
if (!vue.instance) {
vue.instance = new Stat()
}
return vue.instance
}
constructor() {
super()
this.instance = null
}
/**
* 进入应用
* @param {Object} options 页面参数
* @param {Object} self 当前页面实例
*/
launch(options, self) {
// 初始化页面停留时间 start
let residence_time = set_page_residence_time()
this.__licationShow = true
this.sendReportRequest(options, true)
}
load(options, self) {
this.self = self
this._query = options
}
appHide(self){
this.applicationHide(self, true)
}
appShow(self){
this.applicationShow(self)
}
show(self) {
this.self = self
if (get_page_types(self) === 'page') {
this.pageShow(self)
}
if (get_page_types(self) === 'app') {
this.appShow(self)
}
}
hide(self) {
this.self = self
if (get_page_types(self) === 'page') {
this.pageHide(self)
}
if (get_page_types(self) === 'app') {
this.appHide(self)
}
}
error(em) {
// 开发工具内不上报错误
if (this._platform === 'devtools') {
if (process.env.NODE_ENV === 'development') {
console.info('当前运行环境为开发者工具,不上报数据。')
return;
}
}
let emVal = ''
if (!em.message) {
emVal = JSON.stringify(em)
} else {
emVal = em.stack
}
let options = {
ak: this.statData.ak,
uuid: this.statData.uuid,
lt: '31',
ut: this.statData.ut,
ch: this.statData.ch,
mpsdk: this.statData.mpsdk,
mpv: this.statData.mpv,
v: this.statData.v,
em: emVal,
usv: this.statData.usv,
t: parseInt(new Date().getTime() / 1000),
p: this.statData.p,
}
this.request(options)
}
}
export default Stat
import {
get_platform_name,
get_page_vm
} from './utils/pageInfo.js'
import Stat from './core/stat.js'
const stat = Stat.getInstance()
// 用于判断是隐藏页面还是卸载页面
let isHide = false
const lifecycle = {
onLaunch(options) {
// 进入应用上报数据
stat.launch(options, this);
},
onLoad(options) {
stat.load(options, this);
// 重写分享,获取分享上报事件
if (this.$scope && this.$scope.onShareAppMessage) {
let oldShareAppMessage = this.$scope.onShareAppMessage;
this.$scope.onShareAppMessage = function(options) {
stat.interceptShare(false);
return oldShareAppMessage.call(this, options)
}
}
},
onShow() {
isHide = false
stat.show(this);
},
onHide() {
isHide = true
stat.hide(this);
},
onUnload() {
if (isHide) {
isHide = false
return
}
stat.hide(this);
},
onError(e) {
stat.error(e)
}
}
function main() {
if (process.env.NODE_ENV === 'development') {
uni.report = function(type, options) {};
} else {
console.log('统计已开启');
const Vue = require('vue');
(Vue.default || Vue).mixin(lifecycle);
uni.report = function(type, options) {
stat.sendEvent(type, options);
};
}
}
main()
// 访问开始即启动小程序,访问结束结分为:进入后台超过5min、在前台无任何操作超过30min、在新的来源打开小程序;
export const STAT_VERSION = '0.0.1'
export const PAGE_PVER_TIME = 1800 // 页面在前台无操作结束访问时间 单位s
// export const PAGE_PVER_TIME = 0
export const APP_PVER_TIME = 300 // 应用在后台结束访问时间 单位s
// export const APP_PVER_TIME = 0
// export const OPERATING_TIME = 10 // 数据上报时间 单位s
export const OPERATING_TIME = 10 // 数据上报时间 单位s
export const DEBUG = true
export const dbSet = (name, value) => {
let data = uni.getStorageSync('$$STAT__DBDATA') || {}
if (!data) {
data = {}
}
data[name] = value
uni.setStorageSync('$$STAT__DBDATA', data)
}
export const dbGet = (name) => {
let data = uni.getStorageSync('$$STAT__DBDATA') || {}
if (!data) {
data = {}
}
if (!data[name]) {
return undefined
}
return data[name]
}
export const dbRemove = (name) => {
let data = uni.getStorageSync('$$STAT__DBDATA') || {}
if (data[name]) {
delete data[name]
uni.setStorageSync('$$STAT__DBDATA', data)
} else {
data = uni.getStorageSync('$$STAT__DBDATA')
if (data[name]) {
delete data[name]
uni.setStorageSync('$$STAT__DBDATA', data)
}
}
}
// 判断arr是否为一个数组,返回一个bool值
function isArray (arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
// 深度克隆
export const deepClone = (obj)=> {
// 对常见的“非”值,直接返回原来值
if([null, undefined, NaN, false].includes(obj)) return obj;
if(typeof obj !== "object" && typeof obj !== 'function') {
//原始类型直接返回
return obj;
}
var o = isArray(obj) ? [] : {};
for(let i in obj) {
if(obj.hasOwnProperty(i)){
o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i];
}
}
return o;
}
import {deepClone} from './deepClone.js'
// JS对象深度合并
export const deepMerge = (target = {}, source = {})=> {
target = deepClone(target);
if (typeof target !== 'object' || typeof source !== 'object') return false;
for (var prop in source) {
if (!source.hasOwnProperty(prop)) continue;
if (prop in target) {
if (typeof target[prop] !== 'object') {
target[prop] = source[prop];
} else {
if (typeof source[prop] !== 'object') {
target[prop] = source[prop];
} else {
if (target[prop].concat && source[prop].concat) {
target[prop] = target[prop].concat(source[prop]);
} else {
target[prop] = deepMerge(target[prop], source[prop]);
}
}
}
} else {
target[prop] = source[prop];
}
}
return target;
}
// 批量导出文件
const requireApi = require.context('.', false, /.js$/)
let module = {}
let noAllowPaht = ['./index.js']
requireApi.keys().forEach((key, index) => {
if ( noAllowPaht.indexOf(key) !== -1 ) return
Object.assign(module, requireApi(key))
})
export default module
import pagesTitle from 'uni-pages?{"type":"style"}'
let pagesData = pagesTitle.pages
let titleJsons = {}
for (let i in pagesData) {
titleJsons[i] = pagesData[i].navigationBarTitleText || ''
}
import {
sys
} from './util.js'
import {
STAT_URL,
STAT_VERSION,
DIFF_TIME
} from '../config.js';
const UUID_KEY = '__DC_STAT_UUID'
const UUID_VALUE = '__DC_UUID_VALUE'
function getUuid() {
let uuid = ''
if (get_platform_name() === 'n') {
try {
uuid = plus.runtime.getDCloudId()
} catch (e) {
uuid = ''
}
return uuid
}
try {
uuid = uni.getStorageSync(UUID_KEY)
} catch (e) {
uuid = UUID_VALUE
}
if (!uuid) {
uuid = Date.now() + '' + Math.floor(Math.random() * 1e7)
try {
uni.setStorageSync(UUID_KEY, uuid)
} catch (e) {
uni.setStorageSync(UUID_KEY, UUID_VALUE)
}
}
return uuid
}
export const get_uuid = (statData) => {
// 有可能不存在 deviceId(一般不存在就是出bug了),就自己生成一个
return sys.deviceId || getUuid()
}
export const get_sgin = (statData) => {
let arr = Object.keys(statData)
let sortArr = arr.sort()
let sgin = {}
let sginStr = ''
for (var i in sortArr) {
sgin[sortArr[i]] = statData[sortArr[i]]
sginStr += sortArr[i] + '=' + statData[sortArr[i]] + '&'
}
return {
sign: '',
options: sginStr.substr(0, sginStr.length - 1),
}
}
export const get_encodeURIComponent_options = (statData) => {
let data = {}
for (let prop in statData) {
data[prop] = encodeURIComponent(statData[prop])
}
return data
}
/**
* 获取当前平台
* 移动端 : 'n',
* h5 : 'h5',
* 微信 : 'wx',
* 阿里 : 'ali',
* 百度 : 'bd',
* 头条 : 'tt',
* qq : 'qq',
* 快应用 : 'qn',
* 快手 : 'ks',
* 飞书 : 'lark',
* 快应用 : 'qw',
* 钉钉 : 'dt'
*/
export const get_platform_name = () => {
// 苹果审核代码中禁止出现 alipay 字样 ,需要特殊处理一下
const aliArr = ['y', 'a', 'p', 'mp-ali']
const platformList = {
'app': 'n',
'app-plus': 'n',
h5: 'h5',
'mp-weixin': 'wx',
[aliArr.reverse().join('')]: 'ali',
'mp-baidu': 'bd',
'mp-toutiao': 'tt',
'mp-qq': 'qq',
'quickapp-native': 'qn',
'mp-kuaishou': 'ks',
'mp-xhs': 'xhs',
'mp-lark': 'lark',
'quickapp-webview': 'qw'
}
if (platformList[process.env.VUE_APP_PLATFORM] === 'ali') {
if (my && my.env) {
const clientName = my.env.clientName
if (clientName === 'ap') return 'ali'
if (clientName === 'dingtalk') return 'dt'
// TODO 缺少 ali 下的其他平台
}
}
return platformList[process.env.VUE_APP_PLATFORM]
}
/**
* 获取小程序 appid
*/
export const get_pack_name = () => {
let packName = ''
if (get_platform_name() === 'wx' || get_platform_name() === 'qq') {
// 兼容微信小程序低版本基础库
if (uni.canIUse('getAccountInfoSync')) {
packName = uni.getAccountInfoSync().miniProgram.appId || ''
}
}
if (get_platform_name() === 'n') {
// TODO APP 获取包名
}
return packName
}
/**
* 应用版本
*/
export const get_version = () => {
return get_platform_name() === 'n' ? plus.runtime.version : ''
}
/**
* 获取渠道
*/
export const get_channel = () => {
const platformName = get_platform_name()
let channel = ''
if (platformName === 'n') {
channel = plus.runtime.channel
}
if (platformName === 'wx') {
// TODO 需要调研小程序二维码渠道如何获取;
}
return channel
}
/**
* 获取小程序场景值
* @param {Object} options 页面信息
*/
export const get_scene = (options) => {
const platformName = get_platform_name()
let scene = ''
if (options) {
return options
}
if (platformName === 'wx') {
scene = uni.getLaunchOptionsSync().scene
}
return scene
}
/**
* 获取拼接参数
*/
export const get_splicing = (data) => {
let str = ''
for (var i in data) {
str += i + '=' + data[i] + '&'
}
return str.substr(0, str.length - 1)
}
/**
* 获取页面url,不包含参数
*/
export const get_route = (pageVm) => {
let _self = pageVm || get_page_vm();
if (get_platform_name() === 'bd') {
let mp_route = _self.$mp && _self.$mp.page && _self.$mp.page.is;
let scope_route = _self.$scope && _self.$scope.is;
return mp_route || scope_route || ''
} else {
return _self.route || (_self.$scope && _self.$scope.route) || (_self.$mp && _self.$mp.page.route)
}
};
/**
* 获取页面url, 包含参数
*/
export const get_page_route = (pageVm) => {
// 从 app 进入应用 ,没有 $page ,获取不到路由 ,需要获取页面 尝试从 getCurrentPages 获取也页面实例
// FIXME 尽量不使用 getCurrentPages ,大部分获取路由是从 onHide 获取 ,这时可以获取到,如果是 onload ,则可能获取不到,比如 百度
let page = pageVm.$page || (pageVm.$scope && pageVm.$scope.$page)
let lastPageRoute = uni.getStorageSync('_STAT_LAST_PAGE_ROUTE');
if (!page) return lastPageRoute || ''
return page.fullPath === '/' ? page.route : page.fullPath
};
/**
* 获取页面实例
*/
export const get_page_vm = () => {
let pages = getCurrentPages()
let $page = pages[pages.length - 1]
if (!$page) return null
return $page.$vm
}
/**
* 获取页面类型
*/
export const get_page_types = (self) => {
// XXX 百度有问题 ,获取的都是 componet ,等待修复
if (self.mpType === 'page' || self.$mpType === 'page' || (self.$mp && self.$mp.mpType === 'page') || self
.$options.mpType === 'page') {
return 'page';
}
if (self.mpType === 'app' || self.$mpType === 'app' || (self.$mp && self.$mp.mpType === 'app') || self.$options
.mpType === 'app') {
return 'app'
}
return null;
}
/**
* 处理上报参数
* @param {Object} 需要处理的数据
*/
export const handle_data = (statData) => {
let firstArr = []
let contentArr = []
let lastArr = []
for (let i in statData) {
const rd = statData[i]
rd.forEach((elm) => {
const newData = get_splicing(elm)
if (i === 0) {
firstArr.push(newData)
} else if (i === 3) {
lastArr.push(newData)
} else {
contentArr.push(newData)
}
})
}
firstArr.push(...contentArr, ...lastArr)
// 参数需要处理成字符串,方便上传
return JSON.stringify(firstArr)
}
/**
* 自定义事件参数校验
*/
export const calibration = (eventName, options) => {
// login 、 share 、pay_success 、pay_fail 、register 、title
if (!eventName) {
console.error(`uni.report Missing [eventName] parameter`);
return true
}
if (typeof eventName !== 'string') {
console.error(`uni.report [eventName] Parameter type error, it can only be of type String`);
return true
}
if (eventName.length > 255) {
console.error(`uni.report [eventName] Parameter length cannot be greater than 255`);
return true
}
if (typeof options !== 'string' && typeof options !== 'object') {
console.error('uni.report [options] Parameter type error, Only supports String or Object type');
return true
}
if (typeof options === 'string' && options.length > 255) {
console.error(`uni.report [options] Parameter length cannot be greater than 255`);
return true
}
if (eventName === 'title' && typeof options !== 'string') {
console.error(
`uni.report [eventName] When the parameter is title, the [options] parameter can only be of type String`
);
return true
}
}
export const get_page_name = (routepath) => {
return (titleJsons && titleJsons[routepath]) || ''
}
const Report_Data_Time = 'Report_Data_Time'
const Report_Status = 'Report_Status'
export const is_report_data = () => {
return new Promise((resolve, reject) => {
let start_time = ''
let end_time = new Date().getTime()
let diff_time = DIFF_TIME
let report_status = 1
try {
start_time = uni.getStorageSync(Report_Data_Time)
report_status = uni.getStorageSync(Report_Status)
} catch (e) {
start_time = ''
report_status = 1
}
if (report_status === '') {
requestData(({ enable }) => {
uni.setStorageSync(Report_Data_Time, end_time)
uni.setStorageSync(Report_Status, enable)
if (enable === 1) {
resolve()
}
})
return
}
if (report_status === 1) {
resolve()
}
if (!start_time) {
uni.setStorageSync(Report_Data_Time, end_time)
start_time = end_time
}
if (end_time - start_time > diff_time) {
requestData(({ enable }) => {
uni.setStorageSync(Report_Data_Time, end_time)
uni.setStorageSync(Report_Status, enable)
})
}
})
}
const requestData = (done) => {
const appid = process.env.UNI_APP_ID
let formData = {
usv: STAT_VERSION,
conf: JSON.stringify({
ak: appid,
}),
}
uni.request({
url: STAT_URL,
method: 'GET',
data: formData,
success: (res) => {
const { data } = res
if (data.ret === 0) {
typeof done === 'function' &&
done({
enable: data.enable,
})
}
},
fail: (e) => {
let report_status_code = 1
try {
report_status_code = uni.getStorageSync(Report_Status)
} catch (e) {
report_status_code = 1
}
if (report_status_code === '') {
report_status_code = 1
}
typeof done === 'function' &&
done({
enable: report_status_code,
})
},
})
}
import {
get_platform_name
} from './pageInfo.js'
import {
dbSet,
dbGet,
dbRemove
} from './db.js'
import {
PAGE_PVER_TIME,
APP_PVER_TIME
} from '../config.js';
// 首次访问时间
const FIRST_VISIT_TIME_KEY = '__first__visit__time'
// 最后访问时间
const LAST_VISIT_TIME_KEY = '__last__visit__time'
/**
* 获取当前时间
*/
export const get_time = () => {
return parseInt(new Date().getTime() / 1000)
}
/**
* 获取首次访问时间
*/
export const get_first_visit_time = () => {
const timeStorge = dbGet(FIRST_VISIT_TIME_KEY)
let time = 0
if (timeStorge) {
time = timeStorge
} else {
time = get_time()
dbSet(FIRST_VISIT_TIME_KEY, time)
// 首次访问需要 将最后访问时间置 0
dbRemove(LAST_VISIT_TIME_KEY)
}
return time
}
/**
* 最后访问时间
*/
export const get_last_visit_time = () => {
const timeStorge = dbGet(LAST_VISIT_TIME_KEY)
let time = 0
if (timeStorge) {
time = timeStorge
}
dbSet(LAST_VISIT_TIME_KEY, get_time())
return time
}
// 页面停留时间记录key
const PAGE_RESIDENCE_TIME = '__page__residence__time'
/**
* 设置页面停留时间
*/
export const set_page_residence_time = () => {
let First_Page_Residence_Time = get_time()
dbSet(PAGE_RESIDENCE_TIME, First_Page_Residence_Time)
return First_Page_Residence_Time
}
/**
* 获取页面停留时间
*/
export const get_page_residence_time = () => {
let Last_Page_Residence_Time = get_time()
let First_Page_Residence_Time = dbGet(PAGE_RESIDENCE_TIME)
return Last_Page_Residence_Time - First_Page_Residence_Time
}
/**
* 获取总访问次数
*/
const TOTAL_VISIT_COUNT = '__total__visit__count'
export const get_total_visit_count = () => {
const timeStorge = dbGet(TOTAL_VISIT_COUNT)
let count = 1
if (timeStorge) {
count = timeStorge
count++
}
dbSet(TOTAL_VISIT_COUNT, count)
return count
}
export const GetEncodeURIComponentOptions = (statData) => {
let data = {}
for (let prop in statData) {
data[prop] = encodeURIComponent(statData[prop])
}
return data
}
let Set__First__Time = 0
let Set__Last__Time = 0
/**
* 获取第一次时间
*/
export const get_first_time = () => {
let time = new Date().getTime()
Set__First__Time = time
Set__Last__Time = 0
return time
}
/**
* 获取最后一次时间
*/
export const get_last_time = () => {
let time = new Date().getTime()
Set__Last__Time = time
return time
}
/**
* 获取页面 \ 应用停留时间
*/
export const get_residence_time = (type) => {
let residenceTime = 0
if (Set__First__Time !== 0) {
residenceTime = Set__Last__Time - Set__First__Time
}
residenceTime = parseInt(residenceTime / 1000)
residenceTime = residenceTime < 1 ? 1 : residenceTime
if (type === 'app') {
let overtime = residenceTime > APP_PVER_TIME ? true : false
return {
residenceTime,
overtime,
}
}
if (type === 'page') {
let overtime = residenceTime > PAGE_PVER_TIME ? true : false
return {
residenceTime,
overtime,
}
}
return {
residenceTime,
}
}
/**
* 获取系统信息
*/
export const sys = uni.getSystemInfoSync()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册