hookehuyr

feat(埋点): 添加用户行为追踪功能

在CompleteInfoPage和PosterPage中添加用户行为埋点
新增trackingAPI接口用于发送埋点数据
移除login页面中旧的埋点逻辑
/*
* @Date: 2025-12-19 10:43:09
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-26 11:44:09
* @LastEditTime: 2025-12-26 16:00:49
* @FilePath: /mlaj/src/api/recall_users.js
* @Description: 引入外部接口, 召回旧用户相关接口
*/
import { fn, fetch } from './fn';
const Api = {
USER_LOGIN: '/srv/?a=user_login',
USER_LOGIN: '/srv/?a=desk_calendar&t=login',
USER_INFO: '/srv/?a=desk_calendar&t=user_info',
USER_SEARCH_OLD_ACTIVITY: '/srv/?a=desk_calendar&t=search_old_activity',
USER_GET_POSTER: '/srv/?a=desk_calendar&t=get_poster',
USER_EDIT_POSTER: '/srv/?a=desk_calendar&t=edit_poster',
USER_GET_SUPPLEMENT: '/srv/?a=desk_calendar&t=get_supplement',
USER_EDIT_SUPPLEMENT: '/srv/?a=desk_calendar&t=edit_supplement',
USER_TRACKING: '/srv/?a=desk_calendar&t=tracking',
}
/**
* @description: 用户登录
* @param: mobile 手机号
* @param: password 用户密码
* @param: sms_code 短信验证码
* @param: entry 进入H5的入口。qrcode=扫二维码进入, poster=通过海报进入
* @param: referrer_user_id 海报创建人ID
*/
export const loginAPI = (params) => fn(fetch.post(Api.USER_LOGIN, params));
......@@ -69,3 +72,11 @@ export const getSupplementAPI = (params) => fn(fetch.get(Api.USER_GET_SUPPLEMENT
* @param: note 补充说明
*/
export const editSupplementAPI = (params) => fn(fetch.post(Api.USER_EDIT_SUPPLEMENT, params));
/**
* @description: 埋点
* @param: event_type 事件类型。edit_user=完善用户信息, share_poster=转发海报
* @param: campaign_id 活动ID(转发海报时填写)
* @param: stu_uid 学员ID(转发海报时填写)
*/
export const trackingAPI = (params) => fn(fetch.post(Api.USER_TRACKING, params));
......
......@@ -83,7 +83,7 @@ import { useRoute, useRouter } from 'vue-router'
import { useTitle } from '@vueuse/core'
import { showToast } from 'vant'
import { updateUserInfoAPI } from '@/api/users'
import { searchOldActivityAPI } from '@/api/recall_users'
import { searchOldActivityAPI, trackingAPI } from '@/api/recall_users'
const starImg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/xing@2x.png'
......@@ -127,6 +127,10 @@ const handleConfirm = async () => {
idcard: form.idCard
})
if (res.code) {
// 埋点
trackingAPI({
event_type: 'edit_user'
})
const activityRes = await searchOldActivityAPI({
name: form.name,
mobile: form.phone,
......
<!--
* @Date: 2025-12-23 15:50:59
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-25 13:22:42
* @LastEditTime: 2025-12-26 16:06:36
* @FilePath: /mlaj/src/views/recall/PosterPage.vue
* @Description: 分享海报页面
-->
......@@ -46,7 +46,7 @@ import { showToast, showLoadingToast } from 'vant'
import { qiniuFileHash } from '@/utils/qiniuFileHash'
import { useAuth } from '@/contexts/auth'
import { getPosterAPI, editPosterAPI } from '@/api/recall_users'
import { getPosterAPI, editPosterAPI, trackingAPI } from '@/api/recall_users'
const $route = useRoute();
const $router = useRouter();
......@@ -167,6 +167,13 @@ const campaign_id = ref($route.query.campaign_id || '')
const title = ref($route.query.title || '活动海报')
onMounted(async () => {
// 埋点
trackingAPI({
event_type: 'share_poster',
campaign_id: campaign_id.value,
stu_uid: stu_uid.value
})
if (stu_uid.value && campaign_id.value) {
const { code, data } = await getPosterAPI({
stu_uid: stu_uid.value,
......
......@@ -50,7 +50,7 @@
点击“下一步”,即表示您同意我们的
<br />
<span class="text-[#FFDD01] cursor-pointer hover:opacity-80 transition-opacity"
@click="showAgreement = true; trackClick('view_agreement_link')">《美乐爱觉宇宙用户协议》</span>
@click="showAgreement = true">《美乐爱觉宇宙用户协议》</span>
</div>
</FrostedGlass>
</div>
......@@ -74,7 +74,7 @@
</div>
<div class="p-4 border-t border-gray-100 bg-white safe-area-bottom">
<van-button block round type="primary" color="linear-gradient(to right, #FFDD01, #E5C600)"
class="!text-white !font-bold !h-[44px]" @click="showAgreement = false; trackClick('agreement_confirm_btn')">
class="!text-white !font-bold !h-[44px]" @click="showAgreement = false">
我已阅读并同意
</van-button>
</div>
......@@ -94,7 +94,7 @@ import VideoBackground from '@/components/ui/VideoBackground.vue'
// 导入接口
import { smsAPI } from '@/api/common'
import { loginAPI, userInfoAPI } from '@/api/recall_users'
import { useTracking } from '@/composables/useTracking'
// 导入图片
const titleImg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/title01@2x.png'
......@@ -103,18 +103,15 @@ const $route = useRoute()
const $router = useRouter()
useTitle('登陆')
// TAG: 埋点
const { trackPageView, trackClick } = useTracking()
onMounted(() => {
// TAG: 埋点, 登录页面加载
trackPageView('login_page')
})
// Form Data
const phone = ref('')
const code = ref('')
const showAgreement = ref(false)
const entry = ref($route.query.entry || '')
const referrer_user_id = ref($route.query.referrer_user_id || '')
// Countdown Logic
const counting = ref(false)
......@@ -125,9 +122,6 @@ let timer = null
* @description 发送验证码
*/
const handleSendCode = async () => {
// TAG: 埋点, 获取验证码按钮点击
trackClick('send_code_btn', { phone: phone.value })
if (!phone.value) {
showToast('请输入手机号')
return
......@@ -166,9 +160,6 @@ const handleSendCode = async () => {
* @description 登录/下一步
*/
const handleLogin = async () => {
// TAG: 埋点, 登录/下一步按钮点击
trackClick('login_next_btn', { phone: phone.value })
if (!phone.value) {
showToast('请输入手机号')
return
......@@ -179,7 +170,7 @@ const handleLogin = async () => {
}
try {
const res = await loginAPI({ mobile: phone.value, sms_code: code.value })
const res = await loginAPI({ mobile: phone.value, sms_code: code.value, entry: entry.value, referrer_user_id: referrer_user_id.value })
if (res.code) {
// 获取data里面的 user_id, HTTP_USER_TOKEN, 并设置到后面所有的请求头里面,headers.User-Id, headers.User-Token
const { user_id, HTTP_USER_TOKEN } = res?.data?.user_info || {};
......