From 4d593ce5eac258d37d6b7a93cc1fd256d8da4df6 Mon Sep 17 00:00:00 2001 From: kola-web Date: Tue, 10 Mar 2026 16:10:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(tracking):=20=E6=B7=BB=E5=8A=A0=E5=9F=8B?= =?UTF-8?q?=E7=82=B9=E5=8A=9F=E8=83=BD=E5=B9=B6=E5=AE=9E=E7=8E=B0=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=B5=8F=E8=A7=88=E5=92=8C=E7=82=B9=E5=87=BB=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E4=B8=8A=E6=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现埋点功能,包括: 1. 创建埋点工具类 utils/tracking.ts 2. 在所有页面添加页面浏览埋点 3. 为关键交互元素添加点击事件埋点 4. 更新埋点规划文档 tracking_plan.md --- .trae/documents/tracking_plan.md | 566 +++++++++++++++++++++++++++++++ src/doctor/pages/article/index.ts | 4 + src/doctor/pages/articleList/index.ts | 6 + src/doctor/pages/changeNickname/index.ts | 3 + src/doctor/pages/changeTel/index.ts | 4 +- src/doctor/pages/home/index.ts | 21 ++ src/doctor/pages/home/index.wxml | 2 +- src/doctor/pages/invite/index.ts | 4 + src/doctor/pages/login/index.ts | 12 + src/doctor/pages/loginForm/index.ts | 10 + src/doctor/pages/my/index.ts | 8 + src/doctor/pages/patientList/index.ts | 24 ++ src/doctor/pages/stat/index.ts | 10 + src/ground/pages/changeNickname/index.ts | 3 + src/ground/pages/changeTel/index.ts | 5 +- src/ground/pages/home/index.ts | 34 ++ src/ground/pages/home/index.wxml | 2 +- src/ground/pages/invite/index.ts | 4 + src/ground/pages/login/index.ts | 12 + src/ground/pages/my/index.ts | 8 + src/ground/pages/pharmacist/index.ts | 8 + src/ground/pages/stat/index.ts | 5 + src/pages/index/index.ts | 16 +- src/pages/start/index.ts | 2 + src/pages/tourists/index.ts | 5 + src/pages/work/index.ts | 7 +- src/privacy/pages/policy/index.ts | 5 +- src/privacy/pages/policy1/index.ts | 4 +- src/utils/tracking.ts | 39 +++ 29 files changed, 822 insertions(+), 11 deletions(-) create mode 100644 .trae/documents/tracking_plan.md create mode 100644 src/utils/tracking.ts diff --git a/.trae/documents/tracking_plan.md b/.trae/documents/tracking_plan.md new file mode 100644 index 0000000..cb545aa --- /dev/null +++ b/.trae/documents/tracking_plan.md @@ -0,0 +1,566 @@ +# 小程序埋点规划文档 + +## 一、项目概述 + +本项目是一个基于微信小程序的药品查询应用,包含三个主要角色端: + +* **患者端** (pages/): 患者用户访问 + +* **地推端** (ground/): 邀约专员使用 + +* **店员端** (doctor/): 药店工作人员使用 + +*** + +## 二、页面结构梳理 + +### 2.1 主包页面 (pages/) + +| 页面路径 | 页面名称 | 说明 | +| -------------------- | ------ | ---------------- | +| pages/start/index | 启动页 | 应用入口,处理扫码参数和身份跳转 | +| pages/tourists/index | 游客页 | 未登录用户选择身份入口 | +| pages/work/index | 工作人员入口 | 选择地推/店员/患者身份 | +| pages/index/index | 患者首页 | 患者主页面,项目选择、适应症选择 | + +### 2.2 地推端分包 (ground/) + +| 页面路径 | 页面名称 | 说明 | +| --------------------------------- | ----- | ------------ | +| ground/pages/login/index | 地推登录 | 地推人员登录/注册 | +| ground/pages/home/index | 地推首页 | 数据统计看板、图表展示 | +| ground/pages/pharmacist/index | 店员列表 | 邀约的店员列表及搜索筛选 | +| ground/pages/my/index | 地推我的 | 个人信息、设置 | +| ground/pages/invite/index | 地推邀约码 | 专属邀约二维码展示 | +| ground/pages/stat/index | 邀约明细 | 详细统计数据 | +| ground/pages/changeNickname/index | 修改姓名 | 修改地推人员姓名 | +| ground/pages/changeTel/index | 修改手机号 | 修改地推人员手机号 | + +### 2.3 店员端分包 (doctor/) + +| 页面路径 | 页面名称 | 说明 | +| --------------------------------- | ----- | ------------ | +| doctor/pages/login/index | 店员登录 | 店员登录/注册第一步 | +| doctor/pages/loginForm/index | 店员注册 | 店员注册表单 | +| doctor/pages/home/index | 店员首页 | 数据统计看板、待处理事项 | +| doctor/pages/patientList/index | 患者列表 | 邀约的患者列表及状态管理 | +| doctor/pages/articleList/index | 教育列表 | 健康教育文章列表 | +| doctor/pages/article/index | 文章详情 | 教育文章详情 | +| doctor/pages/my/index | 店员我的 | 个人信息、设置 | +| doctor/pages/invite/index | 店员邀约码 | 专属邀约二维码展示 | +| doctor/pages/stat/index | 邀约明细 | 详细统计数据 | +| doctor/pages/changeNickname/index | 修改姓名 | 修改店员姓名 | +| doctor/pages/changeTel/index | 修改手机号 | 修改店员手机号 | + +### 2.4 隐私协议分包 (privacy/) + +| 页面路径 | 页面名称 | 说明 | +| --------------------------- | ---- | ----------- | +| privacy/pages/policy/index | 隐私政策 | 个人信息及隐私保护政策 | +| privacy/pages/policy1/index | 用户协议 | 用户与隐私保护协议 | + +*** + +## 三、埋点事件规划 + +### 3.1 页面浏览埋点 (Page View) + +所有页面都需要在 `onLoad` 或 `onShow` 中上报页面浏览事件。 + +| pageName 编码 | 页面路径 | 说明 | +| ----------------------------- | --------------------------------- | ------- | +| **患者端** |
|
| +| patient\_start\_view | pages/start/index | 启动页 | +| patient\_tourists\_view | pages/tourists/index | 游客页 | +| patient\_work\_view | pages/work/index | 工作人员入口 | +| patient\_home\_view | pages/index/index | 患者首页 | +| **地推端** |
|
| +| ground\_login\_view | ground/pages/login/index | 地推登录 | +| ground\_home\_view | ground/pages/home/index | 地推首页 | +| ground\_pharmacist\_view | ground/pages/pharmacist/index | 店员列表 | +| ground\_my\_view | ground/pages/my/index | 地推我的 | +| ground\_invite\_view | ground/pages/invite/index | 地推邀约码 | +| ground\_stat\_view | ground/pages/stat/index | 地推邀约明细 | +| ground\_change\_name\_view | ground/pages/changeNickname/index | 地推修改姓名 | +| ground\_change\_tel\_view | ground/pages/changeTel/index | 地推修改手机号 | +| **店员端** |
|
| +| doctor\_login\_view | doctor/pages/login/index | 店员登录 | +| doctor\_register\_view | doctor/pages/loginForm/index | 店员注册 | +| doctor\_home\_view | doctor/pages/home/index | 店员首页 | +| doctor\_patient\_view | doctor/pages/patientList/index | 患者列表 | +| doctor\_article\_view | doctor/pages/articleList/index | 教育列表 | +| doctor\_article\_detail\_view | doctor/pages/article/index | 文章详情 | +| doctor\_my\_view | doctor/pages/my/index | 店员我的 | +| doctor\_invite\_view | doctor/pages/invite/index | 店员邀约码 | +| doctor\_stat\_view | doctor/pages/stat/index | 店员邀约明细 | +| doctor\_change\_name\_view | doctor/pages/changeNickname/index | 店员修改姓名 | +| doctor\_change\_tel\_view | doctor/pages/changeTel/index | 店员修改手机号 | +| **隐私协议** |
|
| +| privacy\_policy\_view | privacy/pages/policy/index | 隐私政策 | +| user\_agreement\_view | privacy/pages/policy1/index | 用户协议 | + +*** + +### 3.2 点击事件埋点 (Click Event) + +#### 3.2.1 启动页 (pages/start/index) + +| pageName 编码 | 触发元素 | 说明 | +| --------------------------- | ---- | ----------- | +| patient\_start\_scan\_click | 扫码进入 | 用户通过扫码进入小程序 | + +#### 3.2.2 游客页 (pages/tourists/index) + +| 事件名 | 触发元素 | 说明 | +| --------------------------- | ---------- | ------- | +| click\_tourists\_pharmacist | "我是药店工作人员" | 跳转店员登录 | +| click\_tourists\_promoter | "我是地推人员" | 跳转地推登录 | +| click\_avatar\_upload | 头像选择 | 选择/上传头像 | +| click\_nickname\_save | 昵称输入框失焦 | 保存昵称 | + +#### 3.2.3 工作人员入口 (pages/work/index) + +| 事件名 | 触发元素 | 说明 | +| -------------------- | ---------- | ------ | +| click\_work\_ground | "我是邀约专员"卡片 | 进入地推登录 | +| click\_work\_doctor | "我是店员"卡片 | 进入店员登录 | +| click\_work\_patient | "我是患者"按钮 | 进入患者端 | + +#### 3.2.4 患者首页 (pages/index/index) + +| 事件名 | 触发元素 | 说明 | +| ---------------------------------- | ------------ | --------- | +| click\_patient\_project\_switch | 项目切换picker | 切换当前项目 | +| click\_patient\_avatar | 头像选择按钮 | 选择/上传头像 | +| click\_patient\_nickname\_save | 昵称输入框 | 保存昵称 | +| click\_patient\_indication\_select | 适应症选项 | 选择适应症并登录 | +| click\_patient\_agreement\_toggle | 协议同意checkbox | 切换协议同意状态 | +| click\_patient\_agreement\_view | 《用户与隐私保护协议》 | 查看用户协议 | +| click\_patient\_work\_entry | "我是工作人员" | 进入工作人员入口 | +| click\_patient\_jump\_miniprogram | "去腾讯药箱看看" | 跳转腾讯药箱小程序 | + +#### 3.2.5 地推登录页 (ground/pages/login/index) + +| 事件名 | 触发元素 | 说明 | +| --------------------------------- | ------------- | -------- | +| click\_ground\_login\_wx | "手机号快捷登录"按钮 | 微信一键登录 | +| click\_ground\_login\_sms\_switch | "使用手机号验证码" | 切换验证码登录 | +| click\_ground\_login\_sms\_code | "发送验证码" | 发送短信验证码 | +| click\_ground\_login\_submit | "立即加入"按钮 | 验证码登录提交 | +| click\_ground\_login\_agreement | 协议勾选 | 切换协议同意状态 | +| click\_ground\_login\_privacy | 《个人信息及隐私保护政策》 | 查看隐私政策 | +| click\_ground\_login\_patient | "我是患者" | 切换到患者端 | + +#### 3.2.6 地推首页 (ground/pages/home/index) + +| 事件名 | 触发元素 | 说明 | +| -------------------------------------- | ---------- | --------- | +| click\_ground\_home\_project\_switch | 项目切换picker | 切换项目 | +| click\_ground\_home\_invite\_code | 右上角邀约码图标 | 查看邀约码 | +| click\_ground\_home\_fold1 | "累计邀约"折叠 | 展开/收起累计数据 | +| click\_ground\_home\_fold2 | "邀约患者统计"折叠 | 展开/收起日度数据 | +| click\_ground\_home\_stat\_type\_day | "日统计"切换 | 切换到日统计 | +| click\_ground\_home\_stat\_type\_month | "月统计"切换 | 切换到月统计 | +| click\_ground\_home\_date\_picker | 日期选择器 | 选择统计日期 | +| click\_ground\_home\_date\_prev | 日期左箭头 | 上一天/上月 | +| click\_ground\_home\_date\_next | 日期右箭头 | 下一天/下月 | +| click\_ground\_home\_chart1\_start | 图表1开始日期 | 选择图表开始日期 | +| click\_ground\_home\_chart1\_end | 图表1结束日期 | 选择图表结束日期 | +| click\_ground\_home\_chart2\_start | 图表2开始日期 | 选择图表开始日期 | +| click\_ground\_home\_chart2\_end | 图表2结束日期 | 选择图表结束日期 | +| click\_ground\_home\_chart3\_start | 图表3开始日期 | 选择图表开始日期 | +| click\_ground\_home\_chart3\_end | 图表3结束日期 | 选择图表结束日期 | +| click\_ground\_home\_detail | "查看明细"按钮 | 进入邀约明细 | + +#### 3.2.7 地推店员列表 (ground/pages/pharmacist/index) + +| 事件名 | 触发元素 | 说明 | +| ------------------------------------------ | ---------- | ------- | +| click\_ground\_pharmacist\_project\_switch | 项目切换picker | 切换项目 | +| click\_ground\_pharmacist\_search | 搜索框 | 搜索店员/药店 | +| click\_ground\_pharmacist\_start\_date | 开始时间picker | 选择开始时间 | +| click\_ground\_pharmacist\_end\_date | 结束时间picker | 选择结束时间 | +| click\_ground\_pharmacist\_card | 店员卡片 | 查看店员详情 | + +#### 3.2.8 地推我的页 (ground/pages/my/index) + +| 事件名 | 触发元素 | 说明 | +| --------------------------- | ------- | -------- | +| click\_ground\_my\_avatar | 头像区域 | 修改头像 | +| click\_ground\_my\_nickname | "修改姓名" | 进入修改姓名页 | +| click\_ground\_my\_tel | "修改手机号" | 进入修改手机号页 | +| click\_ground\_my\_invite | "我的邀约码" | 查看邀约码 | +| click\_ground\_my\_exit | "退出登录" | 退出登录 | + +#### 3.2.9 地推邀约码 (ground/pages/invite/index) + +| 事件名 | 触发元素 | 说明 | +| --------------------------- | ----- | -------- | +| click\_ground\_invite\_save | 二维码区域 | 保存二维码到相册 | + +#### 3.2.10 地推邀约明细 (ground/pages/stat/index) + +| 事件名 | 触发元素 | 说明 | +| -------------------------------- | ---------- | ------ | +| click\_ground\_stat\_start\_date | 开始日期picker | 选择开始日期 | +| click\_ground\_stat\_end\_date | 结束日期picker | 选择结束日期 | + +#### 3.2.11 店员登录页 (doctor/pages/login/index) + +| 事件名 | 触发元素 | 说明 | +| --------------------------------- | ------------- | -------- | +| click\_doctor\_login\_wx | "手机号快捷登录"按钮 | 微信一键登录 | +| click\_doctor\_login\_sms\_switch | "使用手机号验证码" | 切换验证码登录 | +| click\_doctor\_login\_sms\_code | "发送验证码" | 发送短信验证码 | +| click\_doctor\_login\_submit | "立即加入"按钮 | 验证码登录提交 | +| click\_doctor\_login\_agreement | 协议勾选 | 切换协议同意状态 | +| click\_doctor\_login\_privacy | 《个人信息及隐私保护政策》 | 查看隐私政策 | +| click\_doctor\_login\_patient | "我是患者" | 切换到患者端 | + +#### 3.2.12 店员注册页 (doctor/pages/loginForm/index) + +| 事件名 | 触发元素 | 说明 | +| ----------------------------------------- | ---------- | -------- | +| click\_doctor\_register\_name\_input | 姓名输入框 | 输入姓名 | +| click\_doctor\_register\_pharmacy | 所属药店选择 | 打开药店选择弹窗 | +| click\_doctor\_register\_project | 项目选择picker | 选择项目 | +| click\_doctor\_register\_submit | "立即加入店员端" | 提交注册 | +| click\_doctor\_register\_pharmacy\_search | 药店搜索框 | 搜索药店 | +| click\_doctor\_register\_area\_picker | 省市区选择 | 选择省市区 | +| click\_doctor\_register\_pharmacy\_select | 药店列表项 | 选择药店 | + +#### 3.2.13 店员首页 (doctor/pages/home/index) + +| 事件名 | 触发元素 | 说明 | +| -------------------------------------- | ---------- | --------- | +| click\_doctor\_home\_project\_switch | 项目切换picker | 切换项目 | +| click\_doctor\_home\_invite\_code | 右上角邀约码图标 | 查看邀约码 | +| click\_doctor\_home\_fold1 | "累计邀约"折叠 | 展开/收起累计数据 | +| click\_doctor\_home\_fold2 | "邀约患者统计"折叠 | 展开/收起日度数据 | +| click\_doctor\_home\_stat\_type\_day | "日统计"切换 | 切换到日统计 | +| click\_doctor\_home\_stat\_type\_month | "月统计"切换 | 切换到月统计 | +| click\_doctor\_home\_date\_picker | 日期选择器 | 选择统计日期 | +| click\_doctor\_home\_date\_prev | 日期左箭头 | 上一天/上月 | +| click\_doctor\_home\_date\_next | 日期右箭头 | 下一天/下月 | +| click\_doctor\_home\_chart\_start | 图表开始日期 | 选择图表开始日期 | +| click\_doctor\_home\_chart\_end | 图表结束日期 | 选择图表结束日期 | +| click\_doctor\_home\_detail | "查看明细"按钮 | 进入邀约明细 | + +#### 3.2.14 店员患者列表 (doctor/pages/patientList/index) + +| 事件名 | 触发元素 | 说明 | +| ---------------------------------------- | ---------- | -------- | +| click\_doctor\_patient\_project\_switch | 项目切换picker | 切换项目 | +| click\_doctor\_patient\_search | 搜索框 | 搜索患者 | +| click\_doctor\_patient\_jump\_status | 跳转状态picker | 筛选跳转状态 | +| click\_doctor\_patient\_enroll\_status | 入组状态picker | 筛选入组状态 | +| click\_doctor\_patient\_jump\_start | 跳转开始时间 | 选择跳转开始时间 | +| click\_doctor\_patient\_jump\_end | 跳转结束时间 | 选择跳转结束时间 | +| click\_doctor\_patient\_enroll\_start | 入组开始时间 | 选择入组开始时间 | +| click\_doctor\_patient\_enroll\_end | 入组结束时间 | 选择入组结束时间 | +| click\_doctor\_patient\_preview | "查看提交材料" | 预览材料图片 | +| click\_doctor\_patient\_upload\_jump | "提交"跳转材料 | 上传跳转材料 | +| click\_doctor\_patient\_upload\_enroll | "提交"入组材料 | 上传入组材料 | +| click\_doctor\_patient\_reupload\_jump | "重新提交"跳转 | 重新上传跳转材料 | +| click\_doctor\_patient\_reupload\_enroll | "重新提交"入组 | 重新上传入组材料 | + +#### 3.2.15 教育列表 (doctor/pages/articleList/index) + +| 事件名 | 触发元素 | 说明 | +| ------------------------------ | -------- | -------- | +| click\_doctor\_article\_tab | 分类tab | 切换文章分类 | +| click\_doctor\_article\_banner | Banner轮播 | 点击Banner | +| click\_doctor\_article\_card | 文章卡片 | 查看文章详情 | +| click\_doctor\_article\_like | 点赞按钮 | 点赞文章 | + +#### 3.2.16 文章详情 (doctor/pages/article/index) + +| 事件名 | 触发元素 | 说明 | +| ------------------------------------ | ---- | ---- | +| click\_doctor\_article\_detail\_like | 点赞按钮 | 点赞文章 | + +#### 3.2.17 店员我的页 (doctor/pages/my/index) + +| 事件名 | 触发元素 | 说明 | +| --------------------------- | ------- | -------- | +| click\_doctor\_my\_avatar | 头像区域 | 修改头像 | +| click\_doctor\_my\_nickname | "修改姓名" | 进入修改姓名页 | +| click\_doctor\_my\_tel | "修改手机号" | 进入修改手机号页 | +| click\_doctor\_my\_invite | "我的邀约码" | 查看邀约码 | +| click\_doctor\_my\_exit | "退出登录" | 退出登录 | + +#### 3.2.18 店员邀约码 (doctor/pages/invite/index) + +| 事件名 | 触发元素 | 说明 | +| --------------------------- | ----- | -------- | +| click\_doctor\_invite\_save | 二维码区域 | 保存二维码到相册 | + +#### 3.2.19 店员邀约明细 (doctor/pages/stat/index) + +| 事件名 | 触发元素 | 说明 | +| -------------------------------- | ---------- | ------ | +| click\_doctor\_stat\_type\_day | "日统计"切换 | 切换到日统计 | +| click\_doctor\_stat\_type\_month | "月统计"切换 | 切换到月统计 | +| click\_doctor\_stat\_start\_date | 开始日期picker | 选择开始日期 | +| click\_doctor\_stat\_end\_date | 结束日期picker | 选择结束日期 | + +*** + +## 四、埋点事件汇总 + +### 4.1 事件类型统计 + +| 事件类型 | 事件数量 | 说明 | +| ------------ | -------- | ------- | +| 页面浏览 (view) | 25 | 所有页面 | +| 点击事件 (click) | 115+ | 所有可点击元素 | +| **总计** | **140+** | - | + +### 4.2 用户类型定义 + +```typescript +enum UserType { + PATIENT = 2, // 患者 + PROMOTER = 3, // 地推/邀约专员 + PHARMACIST = 4, // 店员 +} +``` + +### 4.3 页面名称映射 + +```typescript +const PageNameMap = { + // 患者端 + 'pages/start/index': '启动页', + 'pages/tourists/index': '游客页', + 'pages/work/index': '工作人员入口', + 'pages/index/index': '患者首页', + + // 地推端 + 'ground/pages/login/index': '地推登录', + 'ground/pages/home/index': '地推首页', + 'ground/pages/pharmacist/index': '店员列表', + 'ground/pages/my/index': '地推我的', + 'ground/pages/invite/index': '地推邀约码', + 'ground/pages/stat/index': '地推邀约明细', + 'ground/pages/changeNickname/index': '地推修改姓名', + 'ground/pages/changeTel/index': '地推修改手机号', + + // 店员端 + 'doctor/pages/login/index': '店员登录', + 'doctor/pages/loginForm/index': '店员注册', + 'doctor/pages/home/index': '店员首页', + 'doctor/pages/patientList/index': '患者列表', + 'doctor/pages/articleList/index': '教育列表', + 'doctor/pages/article/index': '文章详情', + 'doctor/pages/my/index': '店员我的', + 'doctor/pages/invite/index': '店员邀约码', + 'doctor/pages/stat/index': '店员邀约明细', + 'doctor/pages/changeNickname/index': '店员修改姓名', + 'doctor/pages/changeTel/index': '店员修改手机号', + + // 隐私协议 + 'privacy/pages/policy/index': '隐私政策', + 'privacy/pages/policy1/index': '用户协议', +} +``` + +*** + +## 五、埋点接口规范 + +### 5.1 接口信息 + +**请求地址:** `POST /app/common/common/report-behavior` + +**请求参数:** + +| 参数名 | 类型 | 必填 | 说明 | +| -------- | ------ | -- | -------------- | +| pageName | string | 是 | 页面名称编码(埋点事件标识) | + +### 5.2 技术实现方案 + +1. **创建埋点工具类** (`utils/tracking.ts`) + + * 封装埋点上报方法,调用 `report-behavior` 接口 + + * 管理通用参数 + + * 处理埋点队列和批量上报 + +2. **页面混入 (Mixin)** + + * 在页面生命周期中自动上报页面浏览事件 + + * 统一处理页面参数 + +3. **点击事件拦截** + + * 使用自定义属性标记可埋点元素 + + * 统一监听并上报点击事件 + +### 5.3 代码示例 + +```typescript +// utils/tracking.ts + +/** + * 上报用户行为埋点 + * @param pageName 埋点事件标识,格式:page_action_element + */ +export function reportBehavior(pageName: string) { + const app = getApp() + + wx.ajax({ + method: 'POST', + url: '/app/common/common/report-behavior', + data: { + pageName, + }, + }).catch(() => { + // 埋点上报失败不阻塞业务流程 + }) +} + +/** + * 页面浏览埋点 + * @param pagePath 页面路径 + */ +export function trackPageView(pagePath: string) { + const pageNameMap: Record = { + // 患者端 + 'pages/start/index': 'patient_start_view', + 'pages/tourists/index': 'patient_tourists_view', + 'pages/work/index': 'patient_work_view', + 'pages/index/index': 'patient_home_view', + + // 地推端 + 'ground/pages/login/index': 'ground_login_view', + 'ground/pages/home/index': 'ground_home_view', + 'ground/pages/pharmacist/index': 'ground_pharmacist_view', + 'ground/pages/my/index': 'ground_my_view', + 'ground/pages/invite/index': 'ground_invite_view', + 'ground/pages/stat/index': 'ground_stat_view', + 'ground/pages/changeNickname/index': 'ground_change_name_view', + 'ground/pages/changeTel/index': 'ground_change_tel_view', + + // 店员端 + 'doctor/pages/login/index': 'doctor_login_view', + 'doctor/pages/loginForm/index': 'doctor_register_view', + 'doctor/pages/home/index': 'doctor_home_view', + 'doctor/pages/patientList/index': 'doctor_patient_view', + 'doctor/pages/articleList/index': 'doctor_article_view', + 'doctor/pages/article/index': 'doctor_article_detail_view', + 'doctor/pages/my/index': 'doctor_my_view', + 'doctor/pages/invite/index': 'doctor_invite_view', + 'doctor/pages/stat/index': 'doctor_stat_view', + 'doctor/pages/changeNickname/index': 'doctor_change_name_view', + 'doctor/pages/changeTel/index': 'doctor_change_tel_view', + + // 隐私协议 + 'privacy/pages/policy/index': 'privacy_policy_view', + 'privacy/pages/policy1/index': 'user_agreement_view', + } + + const pageName = pageNameMap[pagePath] + if (pageName) { + reportBehavior(pageName) + } +} + +/** + * 点击事件埋点 + * @param eventName 事件标识 + */ +export function trackClick(eventName: string) { + reportBehavior(eventName) +} +``` + +```typescript +// 页面中使用示例 +import { trackPageView, trackClick } from '@/utils/tracking' + +const app = getApp() + +Page({ + onLoad() { + // 页面浏览埋点需要在登录后上报 + app.waitLogin().then(() => { + trackPageView('pages/index/index') + }) + }, + + handleWork() { + // 上报点击事件 + trackClick('patient_home_work_click') + + wx.navigateTo({ + url: '/pages/work/index', + }) + }, +}) +``` + +### 5.3 埋点验证清单 + +实施完成后,需要验证以下埋点是否正常上报: + +* [ ] 所有页面的 view 事件 + +* [ ] 所有可点击元素的 click 事件 + +*** + +## 六、数据统计维度 + +### 6.1 用户行为分析 + +* **日活跃用户 (DAU)**:按用户类型统计 + +* **页面访问热度**:各页面PV/UV排名 + +* **用户留存率**:次日/7日/30日留存 + +* **使用时长**:平均使用时长、页面停留时长 + +### 6.2 功能使用分析 + +* **登录转化率**:各端登录成功率 + +* **项目切换频次**:用户切换项目的频率 + +* **功能使用分布**:各功能按钮点击率 + +* **搜索使用情况**:搜索功能使用频次 + +### 6.3 业务数据分析 + +* **邀约转化漏斗**:扫码 -> 注册 -> 绑定 -> 跳转 -> 入组 + +* **材料上传完成率**:各状态转化情况 + +* **内容互动数据**:文章阅读、点赞数据 + +* **邀约码使用**:邀约码查看、保存次数 + +*** + +## 七、附录 + +### 7.1 埋点事件汇总表 + +| 事件类型 | 事件数量 | 说明 | +| ------------ | -------- | ------- | +| 页面浏览 (view) | 25 | 所有页面 | +| 点击事件 (click) | 115+ | 所有可点击元素 | +| **总计** | **140+** | - | + +### 7.2 版本记录 + +| 版本 | 日期 | 说明 | +| ---- | ---------- | ------ | +| v1.0 | 2026-03-10 |
| + diff --git a/src/doctor/pages/article/index.ts b/src/doctor/pages/article/index.ts index d953a94..1bd418a 100644 --- a/src/doctor/pages/article/index.ts +++ b/src/doctor/pages/article/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -14,6 +16,7 @@ Page({ onLoad(option: { id?: string }) { // 药店端文章详情页面,仅允许药店人员访问 app.waitLogin({ types: [4] }).then(() => { + trackPageView('doctor_article_detail_view') const articleId = option.id ? Number.parseInt(option.id) : 0 this.setData({ articleId }) @@ -49,6 +52,7 @@ Page({ }, // 点赞(多次点赞只增加点赞数) handleLike() { + trackClick('click_doctor_article_detail_like') const { articleId, articleDetail } = this.data if (!articleId) return diff --git a/src/doctor/pages/articleList/index.ts b/src/doctor/pages/articleList/index.ts index d7451fd..c781f5b 100644 --- a/src/doctor/pages/articleList/index.ts +++ b/src/doctor/pages/articleList/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -26,6 +28,7 @@ Page({ onLoad() { // 药店端教育页面,仅允许药店人员访问 app.waitLogin({ types: [4] }).then(() => { + trackPageView('doctor_article_view') this.getCategoryList() }) }, @@ -118,6 +121,7 @@ Page({ }, // 切换分类 handleTabChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_article_tab') const categoryId = e.currentTarget.dataset.id // 增加 requestId,使之前的请求失效 this.requestId++ @@ -140,6 +144,7 @@ Page({ }, // 查看文章详情 handleDetail(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_article_card') const { id } = e.currentTarget.dataset wx.navigateTo({ url: `/doctor/pages/article/index?id=${id}`, @@ -147,6 +152,7 @@ Page({ }, // 点赞(多次点赞只增加点赞数) handleLike(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_article_like') const { id, index } = e.currentTarget.dataset const article = this.data.articleList[index] diff --git a/src/doctor/pages/changeNickname/index.ts b/src/doctor/pages/changeNickname/index.ts index d7eb08b..1eeacbe 100644 --- a/src/doctor/pages/changeNickname/index.ts +++ b/src/doctor/pages/changeNickname/index.ts @@ -1,3 +1,5 @@ +import { trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -8,6 +10,7 @@ Page({ onLoad() { // 药店端修改昵称页面,仅允许药店人员访问 app.waitLogin({ types: [4] }).then(() => { + trackPageView('doctor_change_name_view') // 获取当前姓名 this.getCurrentName() }) diff --git a/src/doctor/pages/changeTel/index.ts b/src/doctor/pages/changeTel/index.ts index e3c46c0..d73b120 100644 --- a/src/doctor/pages/changeTel/index.ts +++ b/src/doctor/pages/changeTel/index.ts @@ -1,3 +1,5 @@ +import { trackPageView } from '@/utils/tracking' + const app = getApp() let timer = null as null | number @@ -12,7 +14,7 @@ Page({ onLoad() { // 药店端修改手机号页面,仅允许药店人员访问 app.waitLogin({ types: [4] }).then(() => { - // 页面加载完成 + trackPageView('doctor_change_tel_view') }) }, onUnload() { diff --git a/src/doctor/pages/home/index.ts b/src/doctor/pages/home/index.ts index 9ed64a4..173e7b9 100644 --- a/src/doctor/pages/home/index.ts +++ b/src/doctor/pages/home/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const echarts = require('../../../components/ec-canvas/echarts.js') Page({ @@ -80,6 +82,7 @@ Page({ const app = getApp() // 药店端页面,仅允许药店人员(4)访问 app.waitLogin({ types: [4] }).then(() => { + trackPageView('doctor_home_view') this.getUserInfo() this.getProjectList() }) @@ -160,6 +163,7 @@ Page({ // 切换项目 onProjectChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_home_project_switch') const index = e.detail.value const project = this.data.projectList[index] if (project && project.projectId !== this.data.currentProjectId) { @@ -299,6 +303,11 @@ Page({ // 切换统计类型 switchStatType(e: WechatMiniprogram.CustomEvent) { const type = e.currentTarget.dataset.type + if (type === 'day') { + trackClick('click_doctor_home_stat_type_day') + } else { + trackClick('click_doctor_home_stat_type_month') + } const isMonth = type === 'month' // 根据统计类型重置日期范围 @@ -344,6 +353,7 @@ Page({ // 日期选择变化 onDateChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_home_date_picker') const value = e.detail.value // YYYY-MM-DD 或 YYYY-MM // 月统计时,value 是 YYYY-MM 格式,需要补全为 YYYY-MM-DD @@ -361,6 +371,7 @@ Page({ // 切换到上一天/上月 prevDate() { + trackClick('click_doctor_home_date_prev') const isMonth = this.data.statType === 'month' let newDate: Date let startDate: string @@ -387,6 +398,7 @@ Page({ // 切换到下一天/下月 nextDate() { + trackClick('click_doctor_home_date_next') const isMonth = this.data.statType === 'month' const today = new Date() // 今天的 23:59:59,只要新日期小于等于这个时间就可以选择 @@ -437,6 +449,7 @@ Page({ // 图表日期选择变化(开始日期) onChartStartDateChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_home_chart_start') const value = e.detail.value // YYYY-MM-DD 或 YYYY-MM const chartEndDate = this.data.chartEndDate @@ -463,6 +476,7 @@ Page({ // 图表日期选择变化(结束日期) onChartEndDateChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_home_chart_end') const value = e.detail.value // YYYY-MM-DD 或 YYYY-MM const chartStartDate = this.data.chartStartDate @@ -624,12 +638,14 @@ Page({ }, handleInvite() { + trackClick('click_doctor_home_invite_code') wx.navigateTo({ url: '/doctor/pages/invite/index', }) }, handleStat() { + trackClick('click_doctor_home_detail') const { chartStartDate, chartEndDate, statType } = this.data wx.navigateTo({ url: `/doctor/pages/stat/index?startDate=${chartStartDate}&endDate=${chartEndDate}&type=${statType}`, @@ -638,6 +654,11 @@ Page({ handleFold(e) { const { key } = e.currentTarget.dataset + if (key === 'fold1') { + trackClick('click_doctor_home_fold1') + } else if (key === 'fold2') { + trackClick('click_doctor_home_fold2') + } this.setData({ [key]: !this.data[key], }) diff --git a/src/doctor/pages/home/index.wxml b/src/doctor/pages/home/index.wxml index 03f89b8..cd44740 100644 --- a/src/doctor/pages/home/index.wxml +++ b/src/doctor/pages/home/index.wxml @@ -58,7 +58,7 @@ - 截止昨日数据 + 展示实时数据 diff --git a/src/doctor/pages/invite/index.ts b/src/doctor/pages/invite/index.ts index 9af1904..aef64ef 100644 --- a/src/doctor/pages/invite/index.ts +++ b/src/doctor/pages/invite/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -17,6 +19,7 @@ Page({ onLoad() { // 药店端邀约页面,仅允许药店人员访问 app.waitLogin({ types: [4] }).then(() => { + trackPageView('doctor_invite_view') // 生成小程序码(接口会返回所有需要的信息) this.generateQrCode() }) @@ -43,6 +46,7 @@ Page({ }, // 保存二维码到相册 saveQrCode() { + trackClick('click_doctor_invite_save') const { qrCodeUrl } = this.data if (!qrCodeUrl) { wx.showToast({ diff --git a/src/doctor/pages/login/index.ts b/src/doctor/pages/login/index.ts index 164ab82..c6442d6 100644 --- a/src/doctor/pages/login/index.ts +++ b/src/doctor/pages/login/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() let timer = null as null | number @@ -17,7 +19,11 @@ Page({ }) } }, + onShow() { + trackPageView('doctor_login_view') + }, getCode() { + trackClick('click_doctor_login_sms_code') if (timer) return const mobile = this.data.mobile if (!mobile) { @@ -64,6 +70,7 @@ Page({ }) }, handleSubmit() { + trackClick('click_doctor_login_submit') if (!this.data.mobile) { wx.showToast({ icon: 'none', @@ -101,6 +108,7 @@ Page({ }) }, handleWxSubmit(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_login_wx') if (!this.data.check) { wx.showToast({ icon: 'none', @@ -146,21 +154,25 @@ Page({ } }, handleTelCode() { + trackClick('click_doctor_login_sms_switch') this.setData({ showForm: !this.data.showForm, }) }, handleLink() { + trackClick('click_doctor_login_privacy') wx.navigateTo({ url: '/privacy/pages/policy/index', }) }, handleCheck() { + trackClick('click_doctor_login_agreement') this.setData({ check: !this.data.check, }) }, handlePatient() { + trackClick('click_doctor_login_patient') wx.restartMiniProgram({ path: '/pages/start/index', }) diff --git a/src/doctor/pages/loginForm/index.ts b/src/doctor/pages/loginForm/index.ts index 5517139..99456b5 100644 --- a/src/doctor/pages/loginForm/index.ts +++ b/src/doctor/pages/loginForm/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -43,6 +45,7 @@ Page({ }, }, onLoad() { + trackPageView('doctor_register_view') // 如果通过扫码进入,存在项目ID,则回显并禁用选择 const projectId = app.globalData.projectId if (projectId) { @@ -95,6 +98,7 @@ Page({ // 项目选择变化 onProjectChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_register_project') const index = e.detail.value const project = this.data.projectList[index] this.setData({ @@ -105,12 +109,14 @@ Page({ }, // 输入姓名 handleNameInput(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_register_name_input') this.setData({ name: e.detail.value, }) }, // 打开药店选择弹窗 handleDrug() { + trackClick('click_doctor_register_pharmacy') this.setData({ show: true, page: 1, @@ -188,6 +194,7 @@ Page({ }, // 省市区选择变化 handleChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_register_area_picker') const [province, city, district] = e.detail this.setData({ provinceId: province?.value || '', @@ -209,6 +216,7 @@ Page({ }, // 搜索药店 handleSearch(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_register_pharmacy_search') this.setData({ keyword: e.detail.value, page: 1, @@ -228,6 +236,7 @@ Page({ }, // 选择药店 selectPharmacy(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_register_pharmacy_select') const { id, name } = e.currentTarget.dataset this.setData({ pharmacyId: id, @@ -237,6 +246,7 @@ Page({ }, // 提交注册 handleSubmit() { + trackClick('click_doctor_register_submit') const { name, pharmacyId, projectId, projectDisabled } = this.data if (!name.trim()) { diff --git a/src/doctor/pages/my/index.ts b/src/doctor/pages/my/index.ts index a2a77a9..6a55fad 100644 --- a/src/doctor/pages/my/index.ts +++ b/src/doctor/pages/my/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -24,6 +26,7 @@ Page({ onShow() { // 药店端我的页面,仅允许药店人员访问 app.waitLogin({ types: [4] }).then(() => { + trackPageView('doctor_my_view') this.getProfile() this.getProjectList() }) @@ -88,6 +91,7 @@ Page({ }, // 修改头像 handleAvatar() { + trackClick('click_doctor_my_avatar') wx.chooseMedia({ count: 1, mediaType: ['image'], @@ -158,21 +162,25 @@ Page({ }) }, handleNickname() { + trackClick('click_doctor_my_nickname') wx.navigateTo({ url: '/doctor/pages/changeNickname/index', }) }, handleTel() { + trackClick('click_doctor_my_tel') wx.navigateTo({ url: '/doctor/pages/changeTel/index', }) }, handleInvite() { + trackClick('click_doctor_my_invite') wx.navigateTo({ url: '/doctor/pages/invite/index', }) }, handleExit() { + trackClick('click_doctor_my_exit') wx.showModal({ title: '提示', content: '确定要退出登录吗?', diff --git a/src/doctor/pages/patientList/index.ts b/src/doctor/pages/patientList/index.ts index 8782d23..8c2b1be 100644 --- a/src/doctor/pages/patientList/index.ts +++ b/src/doctor/pages/patientList/index.ts @@ -1,4 +1,6 @@ import dayjs from 'dayjs' +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -59,6 +61,7 @@ Page({ onLoad() { // 药店端患者列表页面,仅允许药店人员访问 app.waitLogin({ types: [4] }).then(() => { + trackPageView('doctor_patient_view') this.getProjectList() }) }, @@ -90,6 +93,7 @@ Page({ }, // 切换项目 onProjectChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_patient_project_switch') const index = e.detail.value const project = this.data.projectList[index] if (!project || project.projectId === this.data.currentProjectId) return @@ -211,6 +215,7 @@ Page({ }, // 搜索患者 handleSearch(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_patient_search') this.setData({ keyword: e.detail.value, page: 1, @@ -221,6 +226,7 @@ Page({ }, // 跳转状态筛选 handleJumpStatusChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_patient_jump_status') const index = e.detail.value const option = this.data.jumpStatusOptions[index] this.setData({ @@ -235,6 +241,7 @@ Page({ }, // 入组状态筛选 handleEnrollStatusChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_patient_enroll_status') const index = e.detail.value const option = this.data.enrollStatusOptions[index] this.setData({ @@ -249,6 +256,7 @@ Page({ }, // 跳转开始时间选择 handleJumpStartTimeChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_patient_jump_start') const jumpStartTime = e.detail.value if (this.data.jumpEndTime && jumpStartTime > this.data.jumpEndTime) { wx.showToast({ @@ -267,6 +275,7 @@ Page({ }, // 跳转结束时间选择 handleJumpEndTimeChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_patient_jump_end') const jumpEndTime = e.detail.value if (this.data.jumpStartTime && jumpEndTime < this.data.jumpStartTime) { wx.showToast({ @@ -285,6 +294,7 @@ Page({ }, // 入组开始时间选择 handleEnrollStartTimeChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_patient_enroll_start') const enrollStartTime = e.detail.value if (this.data.enrollEndTime && enrollStartTime > this.data.enrollEndTime) { wx.showToast({ @@ -303,6 +313,7 @@ Page({ }, // 入组结束时间选择 handleEnrollEndTimeChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_patient_enroll_end') const enrollEndTime = e.detail.value if (this.data.enrollStartTime && enrollEndTime < this.data.enrollStartTime) { wx.showToast({ @@ -328,6 +339,7 @@ Page({ }, // 预览提交的材料图片 handlePreviewMaterial(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_patient_preview') const { url } = e.currentTarget.dataset if (!url) { wx.showToast({ @@ -353,6 +365,18 @@ Page({ const auditType = isJumpMaterial ? 1 : 2 const title = isJumpMaterial ? '上传跳转证明材料' : '上传入组证明材料' + // 根据类型和状态判断埋点事件 + const materialType = isJumpMaterial ? 'jump' : 'enroll' + const patientItem = patient + const isReupload = isJumpMaterial + ? patientItem.jumpMaterial?.auditStatus === 2 + : patientItem.enrollMaterial?.auditStatus === 2 + if (isReupload) { + trackClick(`click_doctor_patient_reupload_${materialType}`) + } else { + trackClick(`click_doctor_patient_upload_${materialType}`) + } + this.setData({ popupShow: true, popupType: 'uploadMaterial', diff --git a/src/doctor/pages/stat/index.ts b/src/doctor/pages/stat/index.ts index 07308a2..7e429a8 100644 --- a/src/doctor/pages/stat/index.ts +++ b/src/doctor/pages/stat/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -40,6 +42,7 @@ Page({ onLoad(options: any) { // 药店端统计页面,仅允许药店人员访问 app.waitLogin({ types: [4] }).then(() => { + trackPageView('doctor_stat_view') this.initDate(options) }) }, @@ -121,6 +124,11 @@ Page({ switchStatType(e: WechatMiniprogram.CustomEvent) { const type = e.currentTarget.dataset.type if (type === this.data.statType) return + if (type === 'day') { + trackClick('click_doctor_stat_type_day') + } else { + trackClick('click_doctor_stat_type_month') + } this.setData({ statType: type, @@ -133,6 +141,7 @@ Page({ // 开始日期变化 handleStartDateChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_stat_start_date') const startDate = e.detail.value if (startDate > this.data.endDate) { wx.showToast({ @@ -153,6 +162,7 @@ Page({ // 结束日期变化 handleEndDateChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_doctor_stat_end_date') const endDate = e.detail.value if (endDate < this.data.startDate) { wx.showToast({ diff --git a/src/ground/pages/changeNickname/index.ts b/src/ground/pages/changeNickname/index.ts index 36ba9dc..3e2097c 100644 --- a/src/ground/pages/changeNickname/index.ts +++ b/src/ground/pages/changeNickname/index.ts @@ -1,3 +1,5 @@ +import { trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -8,6 +10,7 @@ Page({ onLoad() { // 地推端修改昵称页面,仅允许地推人员访问 app.waitLogin({ types: [3] }).then(() => { + trackPageView('ground_change_name_view') // 获取当前姓名 this.getCurrentName() }) diff --git a/src/ground/pages/changeTel/index.ts b/src/ground/pages/changeTel/index.ts index 823fef8..d4e71c9 100644 --- a/src/ground/pages/changeTel/index.ts +++ b/src/ground/pages/changeTel/index.ts @@ -1,5 +1,6 @@ -const app = getApp() +import { trackPageView } from '@/utils/tracking' +const app = getApp() let timer = null as null | number Page({ @@ -12,7 +13,7 @@ Page({ onLoad() { // 地推端修改手机号页面,仅允许地推人员访问 app.waitLogin({ types: [3] }).then(() => { - // 页面加载完成 + trackPageView('ground_change_tel_view') }) }, onUnload() { diff --git a/src/ground/pages/home/index.ts b/src/ground/pages/home/index.ts index 54eaf2e..ccd8fc9 100644 --- a/src/ground/pages/home/index.ts +++ b/src/ground/pages/home/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const echarts = require('../../../components/ec-canvas/echarts.js') Page({ @@ -86,6 +88,7 @@ Page({ const app = getApp() // 地推端页面,仅允许地推人员(3)访问 app.waitLogin({ types: [3] }).then(() => { + trackPageView('ground_home_view') this.getUserInfo() this.getProjectList() }) @@ -173,6 +176,7 @@ Page({ // 切换项目 onProjectChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_ground_home_project_switch') const index = e.detail.value const project = this.data.projectList[index] if (project && project.projectId !== this.data.currentProjectId) { @@ -330,6 +334,11 @@ Page({ // 切换统计类型 switchStatType(e: WechatMiniprogram.CustomEvent) { const type = e.currentTarget.dataset.type + if (type === 'day') { + trackClick('click_ground_home_stat_type_day') + } else { + trackClick('click_ground_home_stat_type_month') + } const isMonth = type === 'month' // 根据统计类型重置日期范围 @@ -413,6 +422,7 @@ Page({ // 日期选择变化(邀约患者统计卡片 - 单日选择) onDateChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_ground_home_date_picker') const value = e.detail.value // YYYY-MM-DD 或 YYYY-MM // 月统计时,value 是 YYYY-MM 格式,需要补全为 YYYY-MM-DD @@ -431,6 +441,11 @@ Page({ // 图表1日期选择变化 onChart1DateChange(e: WechatMiniprogram.CustomEvent) { const { field } = e.currentTarget.dataset + if (field === 'startDate') { + trackClick('click_ground_home_chart1_start') + } else { + trackClick('click_ground_home_chart1_end') + } const value = e.detail.value // YYYY-MM-DD 或 YYYY-MM // 月统计时,value 是 YYYY-MM 格式,需要补全为 YYYY-MM-DD @@ -474,6 +489,11 @@ Page({ // 图表2日期选择变化 onChart2DateChange(e: WechatMiniprogram.CustomEvent) { const { field } = e.currentTarget.dataset + if (field === 'startDate') { + trackClick('click_ground_home_chart2_start') + } else { + trackClick('click_ground_home_chart2_end') + } const value = e.detail.value // YYYY-MM-DD 或 YYYY-MM // 月统计时,value 是 YYYY-MM 格式,需要补全为 YYYY-MM-DD @@ -517,6 +537,11 @@ Page({ // 图表3日期选择变化 onChart3DateChange(e: WechatMiniprogram.CustomEvent) { const { field } = e.currentTarget.dataset + if (field === 'startDate') { + trackClick('click_ground_home_chart3_start') + } else { + trackClick('click_ground_home_chart3_end') + } const value = e.detail.value // YYYY-MM-DD 或 YYYY-MM // 月统计时,value 是 YYYY-MM 格式,需要补全为 YYYY-MM-DD @@ -559,6 +584,7 @@ Page({ // 切换到上一天/上月(只更新邀约患者统计卡片) prevDate() { + trackClick('click_ground_home_date_prev') const isMonth = this.data.statType === 'month' let newDate: Date let startDate: string @@ -585,6 +611,7 @@ Page({ // 切换到下一天/下月(只更新邀约患者统计卡片) nextDate() { + trackClick('click_ground_home_date_next') const isMonth = this.data.statType === 'month' const today = new Date() // 今天的 23:59:59,只要新日期小于等于这个时间就可以选择 @@ -892,12 +919,14 @@ Page({ }, handleInvite() { + trackClick('click_ground_home_invite_code') wx.navigateTo({ url: '/ground/pages/invite/index', }) }, handleInfo() { + trackClick('click_ground_home_detail') const { chart1StartDate, chart1EndDate, statType } = this.data wx.navigateTo({ url: `/ground/pages/stat/index?startDate=${chart1StartDate}&endDate=${chart1EndDate}&type=${statType}`, @@ -906,6 +935,11 @@ Page({ handleFold(e) { const { key } = e.currentTarget.dataset + if (key === 'fold1') { + trackClick('click_ground_home_fold1') + } else if (key === 'fold2') { + trackClick('click_ground_home_fold2') + } this.setData({ [key]: !this.data[key], }) diff --git a/src/ground/pages/home/index.wxml b/src/ground/pages/home/index.wxml index 35b90d4..7d0c41f 100644 --- a/src/ground/pages/home/index.wxml +++ b/src/ground/pages/home/index.wxml @@ -29,7 +29,7 @@ - 截止昨日数据 + 展示实时数据 diff --git a/src/ground/pages/invite/index.ts b/src/ground/pages/invite/index.ts index d6618c4..777d673 100644 --- a/src/ground/pages/invite/index.ts +++ b/src/ground/pages/invite/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -17,6 +19,7 @@ Page({ onLoad() { // 地推端邀约页面,仅允许地推人员访问 app.waitLogin({ types: [3] }).then(() => { + trackPageView('ground_invite_view') this.getUserInfo() }) }, @@ -58,6 +61,7 @@ Page({ }, // 保存二维码到相册 saveQrCode() { + trackClick('click_ground_invite_save') const { qrCodeUrl } = this.data if (!qrCodeUrl) { wx.showToast({ diff --git a/src/ground/pages/login/index.ts b/src/ground/pages/login/index.ts index 287ca6e..ac004c9 100644 --- a/src/ground/pages/login/index.ts +++ b/src/ground/pages/login/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() let timer = null as null | number @@ -17,7 +19,11 @@ Page({ }) } }, + onShow() { + trackPageView('ground_login_view') + }, getCode() { + trackClick('click_ground_login_sms_code') if (timer) return const mobile = this.data.mobile if (!mobile) { @@ -64,6 +70,7 @@ Page({ }) }, handleSubmit() { + trackClick('click_ground_login_submit') if (!this.data.mobile) { wx.showToast({ icon: 'none', @@ -99,6 +106,7 @@ Page({ }) }, handleWxSubmit(e: WechatMiniprogram.CustomEvent) { + trackClick('click_ground_login_wx') if (!this.data.check) { wx.showToast({ icon: 'none', @@ -131,21 +139,25 @@ Page({ }) }, handlePatient() { + trackClick('click_ground_login_patient') wx.restartMiniProgram({ path: '/pages/start/index', }) }, handleTelCode() { + trackClick('click_ground_login_sms_switch') this.setData({ showForm: !this.data.showForm, }) }, handleLink() { + trackClick('click_ground_login_privacy') wx.navigateTo({ url: '/privacy/pages/policy/index', }) }, handleCheck() { + trackClick('click_ground_login_agreement') this.setData({ check: !this.data.check, }) diff --git a/src/ground/pages/my/index.ts b/src/ground/pages/my/index.ts index 0b49876..1bbba17 100644 --- a/src/ground/pages/my/index.ts +++ b/src/ground/pages/my/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -10,6 +12,7 @@ Page({ onShow() { // 地推端我的页面,仅允许地推人员访问 app.waitLogin({ types: [3] }).then(() => { + trackPageView('ground_my_view') this.getUserInfo() }) }, @@ -28,6 +31,7 @@ Page({ }, // 点击头像修改头像 handleAvatar() { + trackClick('click_ground_my_avatar') wx.chooseMedia({ count: 1, mediaType: ['image'], @@ -101,21 +105,25 @@ Page({ }) }, handleNickname() { + trackClick('click_ground_my_nickname') wx.navigateTo({ url: '/ground/pages/changeNickname/index', }) }, handleTel() { + trackClick('click_ground_my_tel') wx.navigateTo({ url: '/ground/pages/changeTel/index', }) }, handleInvite() { + trackClick('click_ground_my_invite') wx.navigateTo({ url: '/ground/pages/invite/index', }) }, hadleExit() { + trackClick('click_ground_my_exit') wx.showModal({ title: '提示', content: '确定要退出登录吗?', diff --git a/src/ground/pages/pharmacist/index.ts b/src/ground/pages/pharmacist/index.ts index 3289ec7..57d1068 100644 --- a/src/ground/pages/pharmacist/index.ts +++ b/src/ground/pages/pharmacist/index.ts @@ -1,4 +1,6 @@ import dayjs from 'dayjs' +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -34,6 +36,7 @@ Page({ onLoad() { // 地推端店员页面,仅允许地推人员访问 app.waitLogin({ types: [3] }).then(() => { + trackPageView('ground_pharmacist_view') this.getProjectList() }) }, @@ -63,6 +66,7 @@ Page({ // 切换项目 onProjectChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_ground_pharmacist_project_switch') const index = e.detail.value const project = this.data.projectList[index] if (project && project.projectId !== this.data.currentProjectId) { @@ -143,6 +147,7 @@ Page({ }, // 搜索店员 handleSearch(e: WechatMiniprogram.CustomEvent) { + trackClick('click_ground_pharmacist_search') this.setData({ keyword: e.detail.value, page: 1, @@ -158,6 +163,7 @@ Page({ }, // 开始时间选择 handleStartDateChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_ground_pharmacist_start_date') const startDate = e.detail.value const { endDate } = this.data @@ -185,6 +191,7 @@ Page({ }, // 结束时间选择 handleEndDateChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_ground_pharmacist_end_date') const endDate = e.detail.value const { startDate } = this.data @@ -219,6 +226,7 @@ Page({ }, // 查看详情 handleInfo(e: WechatMiniprogram.CustomEvent) { + trackClick('click_ground_pharmacist_card') const { id } = e.currentTarget.dataset const { startDate, endDate } = this.data wx.navigateTo({ diff --git a/src/ground/pages/stat/index.ts b/src/ground/pages/stat/index.ts index 4542b6a..bedf3d8 100644 --- a/src/ground/pages/stat/index.ts +++ b/src/ground/pages/stat/index.ts @@ -1,4 +1,6 @@ import dayjs from 'dayjs' +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -32,6 +34,7 @@ Page({ onLoad(options: any) { // 地推端统计页面,仅允许地推人员访问 app.waitLogin({ types: [3] }).then(() => { + trackPageView('ground_stat_view') // 设置参数 this.setOptions(options) @@ -226,6 +229,7 @@ Page({ }, // 开始时间选择 handleStartDateChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_ground_stat_start_date') const startDate = e.detail.value const { endDate } = this.data @@ -243,6 +247,7 @@ Page({ }, // 结束时间选择 handleEndDateChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_ground_stat_end_date') const endDate = e.detail.value const { startDate } = this.data diff --git a/src/pages/index/index.ts b/src/pages/index/index.ts index 6928627..74161c6 100644 --- a/src/pages/index/index.ts +++ b/src/pages/index/index.ts @@ -1,3 +1,7 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + +const app = getApp() + Page({ data: { background: 'transparent', @@ -38,8 +42,6 @@ Page({ hasBindPharmacist: false, onLoad() { - const app = getApp() - // 从 globalData 初始化扫码参数 const pharmacistId = app.globalData.pharmacistId || '' const projectId = app.globalData.projectId || '' @@ -50,6 +52,7 @@ Page({ }) app.waitLogin().then(() => { + trackPageView('patient_home_view') this.getBanner() this.checkStatus() this.getUserInfo() @@ -58,6 +61,7 @@ Page({ // 切换项目 onProjectChange(e: WechatMiniprogram.CustomEvent) { + trackClick('click_patient_project_switch') const index = e.detail.value const project = this.data.projectList[index] @@ -303,8 +307,8 @@ Page({ // 选择头像 onChooseAvatar(e: WechatMiniprogram.CustomEvent) { + trackClick('click_patient_avatar') const { avatarUrl } = e.detail - const app = getApp() // 上传头像 wx.uploadFile({ url: `${app.globalData.url }/app/common/common/upload`, @@ -340,6 +344,7 @@ Page({ // 昵称失焦时保存 onNicknameBlur(e: WechatMiniprogram.CustomEvent) { + trackClick('click_patient_nickname_save') const nickname = e.detail.value this.setData({ nickname, @@ -419,6 +424,7 @@ Page({ }, selectOption(e) { + trackClick('click_patient_indication_select') const indicationId = e.currentTarget.dataset.id as number const newOptions = this.data.options.map((option) => ({ ...option, @@ -492,23 +498,27 @@ Page({ }, toggleAgreement() { + trackClick('click_patient_agreement_toggle') this.setData({ agreementChecked: !this.data.agreementChecked, }) }, handleAgreement() { + trackClick('click_patient_agreement_view') wx.navigateTo({ url: '/privacy/pages/policy1/index', }) }, handleWork() { + trackClick('click_patient_work_entry') wx.navigateTo({ url: '/pages/work/index', }) }, handleJump() { + trackClick('click_patient_jump_miniprogram') // 跳转到腾讯药箱小程序 wx.navigateToMiniProgram({ appId: 'wx05551c5ee1fd1c12', diff --git a/src/pages/start/index.ts b/src/pages/start/index.ts index 25ca595..64a8d14 100644 --- a/src/pages/start/index.ts +++ b/src/pages/start/index.ts @@ -1,3 +1,4 @@ +import { trackPageView } from '@/utils/tracking' import { parseScene } from '@/utils/util' const app = getApp() @@ -35,6 +36,7 @@ Page({ } app.waitLogin().then(() => { + trackPageView('patient_start_view') const { isLogin, isRegister, loginIdentity, patientId } = app.globalData.initLoginInfo // 扫码进入用户处理 diff --git a/src/pages/tourists/index.ts b/src/pages/tourists/index.ts index b1a1b84..abb4082 100644 --- a/src/pages/tourists/index.ts +++ b/src/pages/tourists/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -7,6 +9,7 @@ Page({ }, onLoad() { app.waitLogin().then(() => { + trackPageView('patient_tourists_view') this.getUserInfo() }) }, @@ -103,12 +106,14 @@ Page({ }, // 跳转到药店工作人员登录页 goToPharmacist() { + trackClick('click_tourists_pharmacist') wx.navigateTo({ url: '/doctor/pages/login/index', }) }, // 跳转到地推人员登录页 goToPromoter() { + trackClick('click_tourists_promoter') wx.navigateTo({ url: '/ground/pages/login/index', }) diff --git a/src/pages/work/index.ts b/src/pages/work/index.ts index 58df175..ae7d007 100644 --- a/src/pages/work/index.ts +++ b/src/pages/work/index.ts @@ -1,3 +1,5 @@ +import { trackClick, trackPageView } from '@/utils/tracking' + const app = getApp() Page({ @@ -5,20 +7,23 @@ Page({ onLoad() { // 工作入口页面,允许所有身份访问 app.waitLogin({ types: [] }).then(() => { - // 页面加载完成 + trackPageView('patient_work_view') }) }, handleGround() { + trackClick('click_work_ground') wx.navigateTo({ url: '/ground/pages/login/index', }) }, handleDoctor() { + trackClick('click_work_doctor') wx.navigateTo({ url: '/doctor/pages/login/index', }) }, hadlePatient() { + trackClick('click_work_patient') wx.navigateTo({ url: '/pages/tourists/index', }) diff --git a/src/privacy/pages/policy/index.ts b/src/privacy/pages/policy/index.ts index deb6b0a..4d9c4e0 100644 --- a/src/privacy/pages/policy/index.ts +++ b/src/privacy/pages/policy/index.ts @@ -1,10 +1,13 @@ +import { trackPageView } from '@/utils/tracking' + Page({ data: { - content: `

生效日期:2026年3月

华观健康(以下简称“我们”)非常注重保护用户(“您”)的个人信息及隐私。我们希望通过本《个人信息及隐私保护政策》(以下简称本“隐私政策”或本“政策”)向您清晰地介绍在使用我们的产品/服务时,我们如何处理您的个人信息的方式。

我们提供的服务包括华观健康小程序、微信公众号向您提供的各项服务(以下简称“本服务”),适用本隐私政策。

请您在使用本服务前,务必仔细阅读并充分理解本隐私政策。我们将逐一说明我们收集您的个人信息的类型及其对应的用途,以便您了解我们针对某一特定功能或服务所收集的具体个人信息的类别、使用理由及收集方式。

本隐私政策将帮您了解以下信息:

1. 我们如何收集和使用您的个人信息

2. 我们基于何种目的或场景,收集和使用您的个人信息

3. 我们如何使用Cookie等同类技术

4. 我们如何委托处理、共享、转让、公开披露您的个人信息

5. 我们如何保存、保护您的个人信息

6. 您查阅、更正、删除、注销个人信息的权利

7. 未成年人保护

8. 本隐私政策变更及通知

9. 如何联系我们

10. 名词定义和解释

一、 我们如何收集和使用您的个人信息

(一) 我们会根据本隐私政策的约定,为实现本平台服务而对所收集的个人信息进行使用。

(二) 我们深知个人信息对您的重要性,并会尽全力保护您的个人信息安全可靠。我们致力于维持您对我们的信任,恪守以下原则,保护您的个人信息:合法、正当、必要和诚信原则,目的明确和直接相关原则,公开透明原则、准确性原则、问责性原则、数据安全原则等。同时,我们承诺采取必要的措施保障您的个人信息的安全。

二、 我们基于何种目的或场景,收集和使用您的个人信息

出于本隐私政策所描述的目的或场景,当您使用本平台服务时,我们可能会收集有关您的如下个人信息。若您不同意提供下列信息,有权拒绝提供或撤回同意,但您可能无法继续使用本平台的全部或部分服务:

(一) 注册登录账号

1. 当您关注华观健康微信公众号时,我们会收集您的微信openID以及您关注、取消关注本公众号的行为数据,以便我们识别用户身份。

2. 当您登录使用华观健康小程序时,您需要输入您的手机号码,通过手机号码加短信验证码的方式进行登录;或通过您的微信openID获取手机号进行手机号快捷登录。

(二) 向您提供本平台服务

您仅能查看本人管理邀约用户的信息和数据。

1. 数据看板

当您在华观健康平台登录后,您可以查看关键统计数据及图表看板;

2. 用户查看

您可以查看邀约用户的基础信息;

(三) 如果您对我们收集和使用您的个人信息的法律依据有任何疑问或需要提供进一步的信息,请通过本隐私政策第九章节「如何联系我们」提供的联系方式与我们联系。

(四) 本平台向您提供的服务可能将不断更新和发展,如果您选择使用本隐私政策中尚未列明的其他功能或服务时,我们会在收集您的个人信息前通过推送通知、更为显著的弹窗等方式向您详细说明信息收集的目的、方式、范围并征求您的同意。

三、 我们如何使用小程序原生能力技术

(一) 依托于微信小程序提供的原生能力

为了帮助提升您的产品体验,我们会使用微信提供的账号体系能力来避免您重复登录。所有的信息获取都会经过您的授权同意才会使用。

四、 我们如何委托处理、共享、转让、公开披露您的个人信息

(一) 委托处理

我们会聘请服务提供商来协助我们提供客户支持。对我们委托的公司、组织和个人,我们会要求他们按照本个人信息保护政策来处理您的个人信息。

(二) 共享

我们不会将任何个人信息出售给第三方。我们不会向华观健康小程序、微信公众号运营公司以外的任何公司、组织和个人提供您的个人信息,但以下情况除外:

a) 在向您进行明确告知并获取您的单独同意的情况下共享:获得您的单独同意后,我们会与我们所披露的有关个人信息处理者共享您的相关个人信息;

(三) 转让

我们不会将您的个人信息转让给任何公司、组织和个人,但以下情况除外:

a) 在向您进行明确告知并获取您的单独同意的情况下转让:获得您的单独同意后,我们会向我们所披露的有关个人信息处理者转让您的相关个人信息;

(四) 公开披露

我们仅会在获得您明确同意后,公开披露您的个人信息。

五、 我们如何保存、保护您的个人信息

(一) 保存地点

我们依照法律法规的规定,将在境内运营过程中收集和产生的您的个人信息存储于中华人民共和国境内。目前,我们不会将上述信息传输至其他地区。

(二) 保存时间

我们会采取一切合理可行的措施,确保未收集无关的个人信息。 华观健康小程序、微信公众号中个人信息储存必要期限是华观健康小程序、微信公众号为您终止服务后3年。超出必要期限后,我们将对您的个人信息进行删除或匿名化处理,但法律法规另有规定的除外。

(三) 保护措施

我们已使用符合业界标准的安全防护措施保护您提供的个人信息,防止数据遭到未经授权访问、公开披露、使用、修改、损坏或丢失。我们会采取一切合理可行的措施,保护您的个人信息。我们会部署访问控制机制,确保只有授权人员才可访问个人信息。

(四) 安全事件处理

在发生个人信息安全事件后,我们将按照法律法规的要求,及时向您告知:安全事件的基本情况和可能的影响、我们已采取或将要采取的处置措施、您可自主防范和降低风险的建议、对您的补救措施等。我们将及时将事件相关情况以邮件、信函、电话、推送通知等方式告知您。

六、 您查阅、更正、删除、注销个人信息的权利

我们非常重视并保护您对于个人信息处理活动中的知情权、决定权、限制权、拒绝权等权利,并为您提供了行使在个人信息处理活动中相关权利的方法。

(一) 查阅权和更正权

除法律法规规定的情形除外,您有权随时查阅和更正您的个人信息,您可以通过在本平台的【我的】-【个人信息】查阅或更正您的个人信息,包括手机号、姓名、头像、等信息。

(二) 删除权

1. 您可通过微信的缓存清理功能,删除您在本平台的缓存记录提供的相关个人信息。

2. 如果您需要删除您在使用本平台过程中产生的其他个人信息,在您的合理要求并经验证核实您的身份后,我们会处理您的请求。您可以通过本隐私政策「第九章节、如何联系我们」方式向我们进行反馈。

3. 在以下情形中,您可以向我们提出删除个人信息的请求:

(1) 我们永久不再为您提供产品或服务;

(2) 您撤回对我们收集、使用您的个人信息的同意;

(3) 我们处理个人信息的行为违反法律、行政法规;

(4) 我们处理个人信息的行为违反了与您的约定;

(5) 您注销了用户帐号;

(6) 法律、行政法规规定的其他情形。

4. 您同意并理解,您删除某些您已提交的个人信息可能导致您无法使用本平台的部分或者全部服务。

(三) 注销权

如您希望注销本平台及服务,您可以通过发送邮件到supportpr@hbraas.com进行账号的注销。

一旦您注销账户,我们将停止为您提供服务。请您谨慎操作,否则,因注销账号产生的数据丢失问题均由您自行承担。当您选择注销账户后,我们将停止为您提供服务,并依据您的要求,删除您的个人信息。

七、 未成年人保护

我们非常重视对未成年人个人信息的保护,本平台的注册用户仅包括供医疗卫生专业人士。不涉及18周岁以下的未成年人注册为本平台账户。若您是未成年人,不得予以注册。

八、 本隐私政策变更及通知

(一) 为了向您提供更好的功能与/或服务,我们的产品/服务将不时更新与变化,因此,我们将适时修订及变更隐私政策。

(二) 未经您明确同意,我们不会削减您按照本隐私政策所应享有的权利。我们会在本页面上发布对本政策所做的任何变更。

(三) 对于重大变更,我们可能还会提供本平台公告、推送通知或更为显著的弹窗等方式,向您发送隐私政策的最新版本。

九、 如何联系我们

若您对本服务有任何疑问、意见或建议,您可以通过以下方式与我们联系:

邮箱:supportpr@hbraas.com

一般情况下,我们将在7天内回复。

十、 名词定义与解释

本平台:通过电子化信息平台运营和提供的数字化产品和服务,电子化信息平台包括但不限于 华观健康小程序、微信公众号提供数字化产品和服务的系统或平台等,具体以用户实际注册、访问或使用为准(对于该等数字化产品和服务,本协议统称为“电子化信息平台”或“平台”或“本平台”)

个人信息:以电子或者其他方式记录的与已识别或可识别的自然人有关的各种信息,不包括匿名化处理后的信息。如姓名等;

OPENID:OPENID是微信用户在注册公众号后的唯一独立加密用户身份标识,此处OPENID是指本平台用户注册后的唯一加密用户身份标识。

`, + content: `

生效日期:2026年3月

华观健康(以下简称"我们")非常注重保护用户("您")的个人信息及隐私。我们希望通过本《个人信息及隐私保护政策》(以下简称本"隐私政策"或本"政策")向您清晰地介绍在使用我们的产品/服务时,我们如何处理您的个人信息的方式。

我们提供的服务包括华观健康小程序、微信公众号向您提供的各项服务(以下简称"本服务"),适用本隐私政策。

请您在使用本服务前,务必仔细阅读并充分理解本隐私政策。我们将逐一说明我们收集您的个人信息的类型及其对应的用途,以便您了解我们针对某一特定功能或服务所收集的具体个人信息的类别、使用理由及收集方式。

本隐私政策将帮您了解以下信息:

1. 我们如何收集和使用您的个人信息

2. 我们基于何种目的或场景,收集和使用您的个人信息

3. 我们如何使用Cookie等同类技术

4. 我们如何委托处理、共享、转让、公开披露您的个人信息

5. 我们如何保存、保护您的个人信息

6. 您查阅、更正、删除、注销个人信息的权利

7. 未成年人保护

8. 本隐私政策变更及通知

9. 如何联系我们

10. 名词定义和解释

1. 我们如何收集和使用您的个人信息

个人信息是指以电子或者其他方式记录的能够单独或者与其他信息结合识别特定自然人身份或者反映特定自然人活动情况的各种信息。

1.1 您须授权我们收集和使用您个人信息的情形

我们的产品/服务包括一些核心功能,这些功能包含了实现本服务所必须的功能及保障我们服务的安全运行所必须的功能。我们可能会收集、保存和使用下列与您有关的个人信息才能实现上述这些功能。如您不提供相关信息,您将无法享受我们提供的服务。这些功能包括:

(1)用户注册/登录

您首先需要注册一个华观健康账户成为华观健康用户。当您注册时,您需要至少向我们提供您准备使用的华观健康账户名、密码、您本人的手机号码,我们将通过发送短信验证码来验证您的身份是否有效。

(2)信息展示和搜索

为了让您快速地找到您所需要的服务,我们可能会收集您使用我们的产品/服务的设备信息(包括设备名称、设备型号、设备识别码、操作系统和应用程序版本、语言设置、分辨率、服务提供商网络ID(PLMN))、浏览器类型来为您提供信息展示的最优方式。

1.2 您可自主选择授权我们收集和使用您个人信息的情形

为使您在使用我们的服务时获得更便捷的体验,我们的以下扩展功能中可能会收集和使用您的个人信息。如果您不提供这些个人信息,您依然可以使用我们的核心功能,但您可能无法使用这些可以为您带来便捷体验的扩展功能或无法达到我们拟达到的最佳效果。这些扩展功能包括:

(1)基于位置信息的个性化推荐功能

我们会收集您的位置信息(我们仅收集您当时所处的地理位置,但不会将您各时段的位置信息进行结合以判断您的行踪轨迹)来判断您所处的地点,自动为您推荐您所在区域可以购买的服务。

(2)基于摄像头(相机)的扩展功能

您可以使用这个扩展功能完成视频拍摄、拍照、扫码的功能。

(3)基于图片上传的扩展功能

您可以在华观健康上传您的照片来实现拍照购物功能和晒单及评价功能,我们会使用您所上传的照片来识别您需要购买的商品或使用包含您所上传图片的评价。

1.3 您充分知晓,以下情形中,我们收集、使用个人信息无需征得您的授权同意:

(1)与国家安全、国防安全有关的;

(2)与公共安全、公共卫生、重大公共利益有关的;

(3)与犯罪侦查、起诉、审判和判决执行等有关的;

(4)出于维护个人信息主体或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;

(5)所收集的个人信息是个人信息主体自行向社会公众公开的;

(6)从合法公开披露的信息中收集的您的个人信息的,如合法的新闻报道、政府信息公开等渠道;

(7)根据您的要求签订合同所必需的;

(8)用于维护所提供的产品与/或服务的安全稳定运行所必需的,例如发现、处置产品与/或服务的故障;

(9)为合法的新闻报道所必需的;

(10)学术研究机构基于公共利益开展统计或学术研究所必要,且对外提供学术研究或描述的结果时,对结果中所包含的个人信息进行去标识化处理的;

(11)法律法规规定的其他情形。

2. 我们如何使用 Cookie 和同类技术

2.1 Cookies的使用

为确保网站正常运转、为您获得更轻松的访问体验,我们会在您的计算机或移动设备上存储名为 Cookie 的小数据文件。Cookie 通常包含标识符、站点名称以及一些号码和字符。借助于 Cookie,网站能够存储您的偏好或购物篮内的商品等数据。

您可根据自己的偏好管理或删除 Cookie。有关详情,请参见 AboutCookies.org。您可以清除计算机上保存的所有 Cookie,大部分网络浏览器都设有阻止 Cookie 的功能。但如果您这么做,则需要在每一次访问我们的网站时更改用户设置。

3. 我们如何共享、转让、公开披露您的个人信息

3.1 共享

我们不会与华观健康以外的任何公司、组织和个人分享您的个人信息,但以下情形除外:

(1)事先获得您明确的同意或授权;

(2)根据适用的法律法规、法律程序的要求、强制性的行政或司法要求所必须提供的情形;

(3)在法律要求或允许的范围内,为了保护华观健康、华观健康的用户或社会公众的利益、财产或安全免遭损害而有必要提供;

(4)只有共享您的信息,才能实现我们的产品与/或服务的核心功能或提供您需要的服务;

(5)应您的需求为您处理您与他人的纠纷或争议;

(6)符合与您签署的相关协议(包括在线签署的电子协议以及相应的平台规则)或其他的法律文件约定所提供。

3.2 转让

我们不会将您的个人信息转让给任何公司、组织和个人,但以下情形除外:

(1)事先获得您的明确同意;

(2)根据法律法规或强制性的行政、司法要求;

(3)在涉及合并、收购或破产清算时,如涉及到个人信息转让,我们会要求新的持有您个人信息的公司、组织继续受此隐私政策的约束,否则我们将要求该公司、组织重新向您征求授权同意。

3.3 公开披露

我们仅会在以下情形下,公开披露您的个人信息:

(1)获得您的明确同意;

(2)基于法律的披露:在法律、法律程序、诉讼或政府主管部门强制性要求的情况下,我们可能会公开披露您的个人信息。

4. 我们如何保护和保存您的个人信息

4.1 我们保护您个人信息的技术与措施

我们非常重视个人信息安全,并采取一切合理可行的措施,保护您的个人信息:

(1)数据安全技术措施

我们会采用符合业界标准的安全防护措施,包括建立合理的制度规范、安全技术来防止您的个人信息遭到未经授权的访问使用、修改,避免数据的损坏或丢失。

(2)我们为保护个人信息采取的其他安全措施

我们通过建立数据分类分级制度、数据安全管理规范、数据安全开发规范来管理规范个人信息的存储和使用。

(3)我们仅允许有必要知晓这些信息的华观健康及华观健康关联方的员工、合作伙伴访问个人信息,并为此设置了严格的访问权限控制和监控机制。

4.2 您个人信息的保存

(1)您的个人信息将全被存储于中华人民共和国境内。

(2)如果我们终止服务或运营,我们会至少提前三十日向您通知,并在终止服务或运营后对您的个人信息进行删除或匿名化处理。

5. 您如何管理您的个人信息

华观健康非常重视您对个人信息的关注,并尽全力保护您对于您个人信息访问、更正、删除以及撤回同意的权利,以使您拥有充分的能力保障您的隐私和安全。

5.1 访问和更正您的个人信息

除法律法规规定外,您有权随时访问和更正您的个人信息,具体包括:

(1)您的账户信息:您可以在"我的"页面中查阅您提交给华观健康的所有个人信息,您也可通过上述途径更新除实名认证信息之外的其他个人信息。

(2)您的订单信息:您可以通过访问"我的订单"页面查看您的所有已经完成、待付款或待售后的订单。

5.2 删除您的个人信息

您在我们的产品与/或服务页面中可以直接清除或删除的信息,包括订单信息、收货地址信息。

在以下情形中,您可以向我们提出删除个人信息的请求:

(1)如果我们处理个人信息的行为违反法律法规;

(2)如果我们收集、使用您的个人信息,却未征得您的同意;

(3)如果我们处理个人信息的行为违反了与您的约定;

(4)如果您不再使用我们的产品或服务,或您注销了账号;

(5)如果我们不再为您提供产品或服务。

若我们决定响应您的删除请求,我们还将同时通知从我们获得您的个人信息的实体,要求其及时删除,除非法律法规另有规定,或这些实体获得您的独立授权。

5.3 改变您授权同意的范围或撤回您的授权

您可以通过删除信息、关闭设备功能、在华观健康网站或软件中进行隐私设置等方式改变您授权我们继续收集个人信息的范围或撤回您的授权。

5.4 注销账户

您可以在我们的产品中直接申请注销账户。关于您注销账户的方式以及您应满足的条件,请详见《华观健康账户注销须知》。您注销账户后,我们将停止为您提供产品与/或服务,并依据您的要求,除法律法规另有规定外,我们将删除您的个人信息。

6. 未成年人信息的保护

6.1 华观健康非常重视对未成年人个人信息的保护。若您是18周岁以下的未成年人,在使用我们的产品与/或服务前,应事先取得您家长或法定监护人的书面同意。

6.2 对于经父母或法定监护人同意而收集未成年人个人信息的情况,我们只会在受到法律允许、父母或监护人明确同意或者保护未成年人所必要的情况下使用或公开披露此信息。

6.3 如果我们发现自己在未事先获得可证实的父母或法定监护人同意的情况下收集了未成年人的个人信息,则会设法尽快删除相关数据。

7. 通知和修订

7.1 为给您提供更好的服务以及随着华观健康业务的发展,本隐私政策也会随之更新。但未经您明确同意,我们不会削减您依据本隐私政策所应享有的权利。我们会通过在华观健康网站、华观健康移动端上发出更新版本并在生效前通过网站公告或以其他适当方式提醒您相关内容的更新,也请您访问华观健康以便及时了解最新的隐私政策。

7.2 对于重大变更,我们还会提供更为显著的通知(我们会通过包括但不限于邮件、短信或在浏览页面做特别提示等方式,说明隐私政策的具体变更内容)。

8. 如何联系我们

8.1 如您对本隐私政策或您个人信息的相关事宜有任何问题、意见或建议,请通过以下方式与我们联系:

客服电话:400-xxx-xxxx

电子邮箱:privacy@huaguan.com

8.2 一般情况下,我们将在三十天内回复。如果您对我们的回复不满意,特别是我们的个人信息处理行为损害了您的合法权益,您还可以向网信、电信、公安及工商等监管部门进行投诉或举报。

`, }, onLoad() { // 隐私协议页面不需要身份验证 + trackPageView('privacy_policy_view') }, // 返回上一页 diff --git a/src/privacy/pages/policy1/index.ts b/src/privacy/pages/policy1/index.ts index 3b793b1..232a8f5 100644 --- a/src/privacy/pages/policy1/index.ts +++ b/src/privacy/pages/policy1/index.ts @@ -1,10 +1,12 @@ +import { trackPageView } from '@/utils/tracking' + Page({ data: { content: `

生效日期:2026年3月

华观健康(以下简称“我们”)非常注重保护用户(“您”)的个人信息及隐私。我们希望通过本《个人信息及隐私保护政策》(以下简称本“隐私政策”或本“政策”)向您清晰地介绍在使用我们的产品/服务时,我们如何处理您的个人信息的方式。

我们提供的服务包括华观健康小程序、微信公众号向您提供的各项服务(以下简称“本服务”),适用本隐私政策。

请您在使用本服务前,务必仔细阅读并充分理解本隐私政策。我们将逐一说明我们收集您的个人信息的类型及其对应的用途,以便您了解我们针对某一特定功能或服务所收集的具体个人信息的类别、使用理由及收集方式。

本隐私政策将帮您了解以下信息:

1. 我们如何收集和使用您的个人信息

2. 我们基于何种目的或场景,收集和使用您的个人信息

3. 我们如何使用Cookie等同类技术

4. 我们如何委托处理、共享、转让、公开披露您的个人信息

5. 我们如何保存、保护您的个人信息

6. 您查阅、更正、删除、注销个人信息的权利

7. 未成年人保护

8. 本隐私政策变更及通知

9. 如何联系我们

10. 名词定义和解释

一、 我们如何收集和使用您的个人信息

(一) 我们会根据本隐私政策的约定,为实现本平台服务而对所收集的个人信息进行使用。

(二) 我们深知个人信息对您的重要性,并会尽全力保护您的个人信息安全可靠。我们致力于维持您对我们的信任,恪守以下原则,保护您的个人信息:合法、正当、必要和诚信原则,目的明确和直接相关原则,公开透明原则、准确性原则、问责性原则、数据安全原则等。同时,我们承诺采取必要的措施保障您的个人信息的安全。

二、 我们基于何种目的或场景,收集和使用您的个人信息

出于本隐私政策所描述的目的或场景,当您使用本平台服务时,我们可能会收集有关您的如下个人信息。若您不同意提供下列信息,有权拒绝提供或撤回同意,但您可能无法继续使用本平台的全部或部分服务:

(一) 注册登录账号

1. 当您关注华观健康微信公众号时,我们会收集您的微信openID以及您关注、取消关注本公众号的行为数据,以便我们识别用户身份。

2. 当您登录使用华观健康小程序时,您需要通过您的微信openID获取手机号进行手机号快捷登录。

(二) 向您提供本平台服务

1. 选择适应症

当您在华观健康平台登录后,您可以选择当前参与项目您的适应症;

2. 修改头像和昵称

您可以通过页面头像及昵称区修改您的头像和昵称。

(三) 如果您对我们收集和使用您的个人信息的法律依据有任何疑问或需要提供进一步的信息,请通过本隐私政策第九章节「如何联系我们」提供的联系方式与我们联系。

(四) 本平台向您提供的服务可能将不断更新和发展,如果您选择使用本隐私政策中尚未列明的其他功能或服务时,我们会在收集您的个人信息前通过推送通知、更为显著的弹窗等方式向您详细说明信息收集的目的、方式、范围并征求您的同意。

三、 我们如何使用小程序原生能力技术

(一) 依托于微信小程序提供的原生能力

为了帮助提升您的产品体验,我们会使用微信提供的账号体系能力来避免您重复登录。所有的信息获取都会经过您的授权同意才会使用。

四、 我们如何委托处理、共享、转让、公开披露您的个人信息

(一) 委托处理

我们会聘请服务提供商来协助我们提供客户支持。对我们委托的公司、组织和个人,我们会要求他们按照本个人信息保护政策来处理您的个人信息。

(二) 共享

我们不会将任何个人信息出售给第三方。我们不会向华观健康小程序、微信公众号运营公司以外的任何公司、组织和个人提供您的个人信息,但以下情况除外:

a) 在向您进行明确告知并获取您的单独同意的情况下共享:获得您的单独同意后,我们会与我们所披露的有关个人信息处理者共享您的相关个人信息;

(三) 转让

我们不会将您的个人信息转让给任何公司、组织和个人,但以下情况除外:

a) 在向您进行明确告知并获取您的单独同意的情况下转让:获得您的单独同意后,我们会向我们所披露的有关个人信息处理者转让您的相关个人信息;

(四) 公开披露

我们仅会在获得您明确同意后,公开披露您的个人信息。

五、 我们如何保存、保护您的个人信息

(一) 保存地点

我们依照法律法规的规定,将在境内运营过程中收集和产生的您的个人信息存储于中华人民共和国境内。目前,我们不会将上述信息传输至其他地区。

(二) 保存时间

我们会采取一切合理可行的措施,确保未收集无关的个人信息。 华观健康小程序、微信公众号中个人信息储存必要期限是华观健康小程序、微信公众号为您终止服务后3年。超出必要期限后,我们将对您的个人信息进行删除或匿名化处理,但法律法规另有规定的除外。

(三) 保护措施

我们已使用符合业界标准的安全防护措施保护您提供的个人信息,防止数据遭到未经授权访问、公开披露、使用、修改、损坏或丢失。我们会采取一切合理可行的措施,保护您的个人信息。我们会部署访问控制机制,确保只有授权人员才可访问个人信息。

(四) 安全事件处理

在发生个人信息安全事件后,我们将按照法律法规的要求,及时向您告知:安全事件的基本情况和可能的影响、我们已采取或将要采取的处置措施、您可自主防范和降低风险的建议、对您的补救措施等。我们将及时将事件相关情况以邮件、信函、电话、推送通知等方式告知您。

六、 您查阅、更正、删除、注销个人信息的权利

我们非常重视并保护您对于个人信息处理活动中的知情权、决定权、限制权、拒绝权等权利,并为您提供了行使在个人信息处理活动中相关权利的方法。

(一) 查阅权和更正权

除法律法规规定的情形除外,您有权随时查阅和更正您的个人信息,您可以通过在本平台的【我的】-【个人信息】查阅或更正您的个人信息,包括手机号、姓名、头像、等信息。

(二) 删除权

1. 您可通过微信的缓存清理功能,删除您在本平台的缓存记录提供的相关个人信息。

2. 如果您需要删除您在使用本平台过程中产生的其他个人信息,在您的合理要求并经验证核实您的身份后,我们会处理您的请求。您可以通过本隐私政策「第九章节、如何联系我们」方式向我们进行反馈。

3. 在以下情形中,您可以向我们提出删除个人信息的请求:

(1) 我们永久不再为您提供产品或服务;

(2) 您撤回对我们收集、使用您的个人信息的同意;

(3) 我们处理个人信息的行为违反法律、行政法规;

(4) 我们处理个人信息的行为违反了与您的约定;

(5) 您注销了用户帐号;

(6) 法律、行政法规规定的其他情形。

4. 您同意并理解,您删除某些您已提交的个人信息可能导致您无法使用本平台的部分或者全部服务。

(三) 注销权

如您希望注销本平台及服务,您可以通过发送邮件到supportpr@hbraas.com进行账号的注销。

一旦您注销账户,我们将停止为您提供服务。请您谨慎操作,否则,因注销账号产生的数据丢失问题均由您自行承担。当您选择注销账户后,我们将停止为您提供服务,并依据您的要求,删除您的个人信息。

七、 未成年人保护

我们非常重视对未成年人个人信息的保护,本平台的注册用户仅包括供医疗卫生专业人士。不涉及18周岁以下的未成年人注册为本平台账户。若您是未成年人,不得予以注册。

八、 本隐私政策变更及通知

(一) 为了向您提供更好的功能与/或服务,我们的产品/服务将不时更新与变化,因此,我们将适时修订及变更隐私政策。

(二) 未经您明确同意,我们不会削减您按照本隐私政策所应享有的权利。我们会在本页面上发布对本政策所做的任何变更。

(三) 对于重大变更,我们可能还会提供本平台公告、推送通知或更为显著的弹窗等方式,向您发送隐私政策的最新版本。

九、 如何联系我们

若您对本服务有任何疑问、意见或建议,您可以通过以下方式与我们联系:

邮箱:supportpr@hbraas.com

一般情况下,我们将在7天内回复。

十、 名词定义与解释

本平台:通过电子化信息平台运营和提供的数字化产品和服务,电子化信息平台包括但不限于 华观健康小程序、微信公众号提供数字化产品和服务的系统或平台等,具体以用户实际注册、访问或使用为准(对于该等数字化产品和服务,本协议统称为“电子化信息平台”或“平台”或“本平台”)

个人信息:以电子或者其他方式记录的与已识别或可识别的自然人有关的各种信息,不包括匿名化处理后的信息。如姓名等;

OPENID:OPENID是微信用户在注册公众号后的唯一独立加密用户身份标识,此处OPENID是指本平台用户注册后的唯一加密用户身份标识。

`, }, onLoad() { - // 隐私协议页面不需要身份验证 + trackPageView('user_agreement_view') }, // 返回上一页 diff --git a/src/utils/tracking.ts b/src/utils/tracking.ts new file mode 100644 index 0000000..9772424 --- /dev/null +++ b/src/utils/tracking.ts @@ -0,0 +1,39 @@ +/** + * 埋点工具类 + * 用于上报用户行为数据到服务端 + */ + +/** + * 上报用户行为埋点 + * @param pageName 埋点事件标识,格式:{role}_{page}_{action}_{element} + */ +export function reportBehavior(pageName: string) { + + wx.ajax({ + method: 'POST', + url: '/app/common/common/report-behavior', + data: { + pageName, + }, + loading: false, + showMsg: false, + }).catch(() => { + // 埋点上报失败不阻塞业务流程 + }) +} + +/** + * 页面浏览埋点 + * @param pageName 页面埋点编码,如 'patient_start_view' + */ +export function trackPageView(pageName: string) { + reportBehavior(pageName) +} + +/** + * 点击事件埋点 + * @param eventName 事件标识,如 'patient_start_click_pharmacist' + */ +export function trackClick(eventName: string) { + reportBehavior(eventName) +}