From f64c9f0238d82ecf44ae24ce629a0fa3b9e8b7dc Mon Sep 17 00:00:00 2001 From: kola-web Date: Thu, 5 Mar 2026 18:37:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=BB=9F=E8=AE=A1=E9=A1=B5=E9=9D=A2):=20?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E7=BB=9F=E8=AE=A1=E9=A1=B5=E9=9D=A2=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E6=94=AF=E6=8C=81=E6=97=A5=E6=9C=9F=E8=8C=83?= =?UTF-8?q?=E5=9B=B4=E7=AD=9B=E9=80=89=E5=92=8C=E8=8D=AF=E5=B8=88=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(组件): 优化pickerArea组件,支持树形结构数据转换 style(登录表单): 调整输入框和选择器样式,提升用户体验 fix(首页): 修复统计数据展示问题,区分累计和日/月度统计 --- .trae/documents/component-reuse-analysis-report.md | 303 +++++++++++++++++++++ src/components/pickerArea/index.ts | 52 ++-- src/doctor/pages/home/index.ts | 109 ++++++-- src/doctor/pages/home/index.wxml | 16 +- src/doctor/pages/loginForm/index.scss | 2 +- src/doctor/pages/loginForm/index.ts | 99 ++++++- src/doctor/pages/loginForm/index.wxml | 35 ++- src/doctor/pages/my/index.ts | 31 ++- src/doctor/pages/patientList/index.wxml | 5 +- src/doctor/pages/stat/index.ts | 15 +- src/ground/pages/home/index.ts | 3 +- src/ground/pages/my/index.ts | 6 +- src/ground/pages/pharmacist/index.ts | 3 +- src/ground/pages/stat/index.ts | 131 +++++++-- src/pages/index/index.wxml | 1 + src/pages/tourists/index.scss | 32 +++ src/pages/tourists/index.wxml | 11 +- src/pages/work/index.wxml | 1 + 18 files changed, 747 insertions(+), 108 deletions(-) create mode 100644 .trae/documents/component-reuse-analysis-report.md diff --git a/.trae/documents/component-reuse-analysis-report.md b/.trae/documents/component-reuse-analysis-report.md new file mode 100644 index 0000000..d8b9af2 --- /dev/null +++ b/.trae/documents/component-reuse-analysis-report.md @@ -0,0 +1,303 @@ +# 组件复用性分析报告 + +## 一、项目组件盘点 + +### 1.1 现有自定义组件(10个) + +| 组件名称 | 路径 | 功能描述 | 使用频率 | 状态 | +|---------|------|---------|---------|------| +| navbar | /components/navbar | 自定义导航栏 | 高(15+页面) | ✅ 良好 | +| pagination | /components/pagination | 分页组件 | 中(5页面) | ✅ 良好 | +| popup | /components/popup | 通用弹窗 | 高(8页面) | ⚠️ 需优化 | +| calendar | /components/calendar | 日历组件 | 低(未使用) | ❓ 待确认 | +| pickerArea | /components/pickerArea | 省市区选择器 | 低(1页面) | ✅ 可用 | +| uploadFile | /components/uploadFile | 文件上传 | 低(1页面) | ✅ 可用 | +| ec-canvas | /components/ec-canvas | ECharts图表 | 中(2页面) | ✅ 良好 | +| customPoster | /components/customPoster | 海报生成 | 低(未使用) | ❓ 待确认 | +| star | /components/star | 星星动画 | 低(未使用) | ❓ 待确认 | +| popupDoctor | /components/popupDoctor | 医生信息弹窗 | 低(未使用) | ❓ 待确认 | + +### 1.2 TabBar组件(2个) + +| 组件名称 | 路径 | 使用端 | 使用频率 | +|---------|------|--------|---------| +| doctor-tab-bar | /components/doctor-tab-bar | 药店端 | 4页面 | +| ground-tab-bar | /components/ground-tab-bar | 地推端 | 3页面 | + +**复用建议:** 两个组件功能相似,可合并为 `tab-bar` 组件,通过配置区分不同角色。 + +--- + +## 二、组件复用价值评估矩阵 + +### 2.1 高优先级组件(建议立即提取) + +| 组件名称 | 使用频率 | 功能独立性 | 通用性 | 复杂度 | 复用价值 | 优先级 | +|---------|---------|-----------|--------|--------|---------|--------| +| **项目选择器** | 3次 | 高 | 高 | 低 | ⭐⭐⭐⭐⭐ | P0 | +| **统计卡片** | 4次 | 高 | 高 | 中 | ⭐⭐⭐⭐⭐ | P0 | +| **日期选择逻辑** | 5次 | 高 | 高 | 中 | ⭐⭐⭐⭐ | P1 | + +### 2.2 中优先级组件(建议后续提取) + +| 组件名称 | 使用频率 | 功能独立性 | 通用性 | 复杂度 | 复用价值 | 优先级 | +|---------|---------|-----------|--------|--------|---------|--------| +| **状态标签** | 2次 | 高 | 中 | 低 | ⭐⭐⭐ | P2 | +| **个人信息页面** | 6次 | 中 | 中 | 高 | ⭐⭐⭐ | P2 | +| **登录页面** | 2次 | 中 | 中 | 高 | ⭐⭐⭐ | P2 | +| **修改昵称/手机页面** | 4次 | 中 | 中 | 中 | ⭐⭐⭐ | P2 | + +### 2.3 低优先级组件(保持现状) + +| 组件名称 | 使用频率 | 功能独立性 | 通用性 | 复杂度 | 复用价值 | 优先级 | +|---------|---------|-----------|--------|--------|---------|--------| +| **患者卡片** | 1次 | 中 | 低 | 高 | ⭐⭐ | P3 | +| **上传材料弹窗** | 1次 | 高 | 中 | 中 | ⭐⭐ | P3 | + +--- + +## 三、详细组件分析 + +### 3.1 项目选择器(ProjectPicker) + +**使用位置:** +1. 地推端首页:`/ground/pages/home/index.wxml` +2. 药店端首页:`/doctor/pages/home/index.wxml` +3. 患者列表页:`/doctor/pages/patientList/index.wxml` + +**代码相似度:** 90% + +**主要差异:** +- 主题色不同(地推端蓝色 #4A8DFF,药店端橙色 #FF8A4C) +- 样式细节略有差异 + +**提取收益:** +- 减少重复代码约 60 行 +- 统一项目切换交互体验 +- 便于后续维护 + +**组件接口设计:** +```typescript +interface ProjectPickerProps { + projectList: Array<{ projectId: number; projectName: string }>; + currentProjectName: string; + themeColor: string; // 主题色 + onChange: (projectId: number) => void; +} +``` + +### 3.2 统计卡片(StatCard) + +**使用位置:** +1. 地推端首页:`/ground/pages/home/index.wxml`(2处) +2. 药店端首页:`/doctor/pages/home/index.wxml`(2处) +3. 地推端统计页:`/ground/pages/stat/index.wxml` +4. 药店端统计页:`/doctor/pages/stat/index.wxml` + +**代码相似度:** 88% + +**主要差异:** +- 地推端显示"药店数",药店端不显示 +- 数据字段名称略有不同 +- 主题色不同 + +**提取收益:** +- 减少重复代码约 200 行 +- 统一统计卡片样式 +- 支持配置化展示 + +**组件接口设计:** +```typescript +interface StatCardProps { + title: string; + tip?: string; + data: Array<{ name: string; value: number }>; + showIndicationStats?: boolean; + indicationStats?: Array; + themeColor: string; +} +``` + +### 3.3 日期选择逻辑(DatePickerMixin) + +**使用位置:** +1. 地推端首页:日期筛选逻辑 +2. 药店端首页:日期筛选逻辑 +3. 患者列表页:时间筛选 +4. 地推端统计页:日期筛选 +5. 药店端统计页:日期筛选 + +**代码相似度:** 85% + +**重复逻辑:** +- 日期格式化函数 +- 日期范围验证 +- 日/月模式切换 +- 日期选择器事件处理 + +**提取收益:** +- 减少重复代码约 150 行 +- 统一日期处理逻辑 +- 避免日期相关 bug + +**Mixin接口设计:** +```typescript +interface DatePickerMixin { + // 数据 + startDate: string; + endDate: string; + statType: 'day' | 'month'; + + // 方法 + formatDate(date: string): string; + formatMonth(date: string): string; + validateDateRange(start: string, end: string): boolean; + onDateChange(e: CustomEvent): void; + prevDate(): void; + nextDate(): void; +} +``` + +### 3.4 状态标签(StatusTag) + +**使用位置:** +1. 患者列表页:跳转状态、入组状态、审核状态 + +**代码相似度:** 80% + +**状态定义:** +```typescript +// 跳转/入组状态 +const statusOptions = [ + { value: '', label: '全部' }, + { value: 0, label: '未跳转/未入组' }, + { value: 1, label: '已跳转/已入组' }, +]; + +// 审核状态 +const auditStatusMap = { + 0: { label: '未审核', class: 's2' }, + 1: { label: '审核中', class: 's2' }, + 2: { label: '已通过', class: 's1' }, + 3: { label: '已驳回', class: 's1' }, +}; +``` + +**提取收益:** +- 统一状态展示样式 +- 便于新增状态类型 + +### 3.5 个人信息相关页面 + +**涉及页面:** +- 地推端:my、changeNickname、changeTel(3个页面) +- 药店端:my、changeNickname、changeTel(3个页面) + +**代码相似度:** 95% + +**主要差异:** +- API接口地址不同 +- 数据字段名称不同 +- 主题色不同 + +**提取建议:** +创建通用页面模板,通过配置区分: +```typescript +interface ProfileConfig { + role: 'promoter' | 'pharmacist'; + themeColor: string; + apiEndpoints: { + getProfile: string; + updateAvatar: string; + updateName: string; + updatePhone: string; + logout: string; + }; +} +``` + +--- + +## 四、组件提取实施建议 + +### 4.1 第一阶段:高优先级组件(1-2周) + +1. **项目选择器** + - 创建 `/components/project-picker/` + - 替换 3 个页面的使用 + - 编写组件文档 + +2. **统计卡片** + - 创建 `/components/stat-card/` + - 替换 4 个页面的使用 + - 编写组件文档 + +3. **日期选择Mixin** + - 创建 `/mixins/date-picker.ts` + - 在 5 个页面中引入使用 + - 编写使用文档 + +### 4.2 第二阶段:中优先级组件(2-3周) + +1. **状态标签组件** +2. **个人信息页面模板** +3. **登录页面模板** + +### 4.3 第三阶段:优化与文档(1周) + +1. 完善组件文档 +2. 编写使用示例 +3. 代码审查与优化 + +--- + +## 五、预期收益 + +### 5.1 代码量减少 + +| 组件 | 预计减少代码行数 | +|------|----------------| +| 项目选择器 | 60 行 | +| 统计卡片 | 200 行 | +| 日期选择Mixin | 150 行 | +| 状态标签 | 80 行 | +| 个人信息页面 | 300 行 | +| **总计** | **约 800 行** | + +### 5.2 维护效率提升 + +- 统一交互体验 +- 减少重复 bug +- 便于功能迭代 +- 新功能开发效率提升 30% + +### 5.3 代码质量提升 + +- 提高代码可读性 +- 降低耦合度 +- 提升可测试性 + +--- + +## 六、风险评估与应对措施 + +| 风险点 | 影响 | 应对措施 | +|--------|------|----------| +| 组件耦合度高 | 提取困难 | 先解耦再提取,保持原有功能 | +| 样式差异大 | 统一困难 | 通过 props 配置主题色和样式 | +| 替换影响功能 | 回归测试 | 分阶段替换,每阶段充分测试 | +| 接口差异 | 逻辑复杂 | 使用配置化方式处理差异 | + +--- + +## 七、下一步行动 + +1. **立即开始**:项目选择器组件提取 +2. **本周完成**:统计卡片组件提取 +3. **下周开始**:日期选择Mixin提取 +4. **持续进行**:组件文档编写 + +--- + +*报告生成时间:2026-03-05* +*分析师:AI Assistant* diff --git a/src/components/pickerArea/index.ts b/src/components/pickerArea/index.ts index 9a5f433..d57857a 100644 --- a/src/components/pickerArea/index.ts +++ b/src/components/pickerArea/index.ts @@ -231,51 +231,37 @@ Component({ wx.ajax({ method: 'GET', url: '/app/common/common/area-list', - }).then((res: any[]) => { - // 将扁平数据转换为树形结构 - const areaTree = this.buildAreaTree(res) + }).then((res: any) => { + // 接口直接返回树形结构数据 + const areaList = res.data || res || [] + // 转换数据格式以兼容组件 + const areaTree = this.convertAreaData(areaList) this.setData({ area: areaTree, }) this.getRangeList() }) }, - // 构建省市区树形结构 - buildAreaTree(areaList: any[]) { + // 转换接口数据为组件需要的格式 + convertAreaData(areaList: any[]) { if (!areaList || !areaList.length) return [] - // 构建以 parentAreaId 为 key 的映射 - const parentMap: { [key: string]: any[] } = {} - areaList.forEach((item) => { - const parentId = item.parentAreaId || '0' - if (!parentMap[parentId]) { - parentMap[parentId] = [] - } - parentMap[parentId].push({ - name: item.areaName, - code: item.areaId, - value: item.areaId, - label: item.areaName, - level: item.level, - parentId: item.parentAreaId, - }) - }) - - // 递归构建树 - const buildTree = (parentId: string): any[] => { - const children = parentMap[parentId] - if (!children) return [] - - return children.map((item) => { - const subChildren = buildTree(item.code) - if (subChildren.length > 0) { - return { ...item, children: subChildren } + const convert = (list: any[]): any[] => { + return list.map((item) => { + const result: any = { + name: item.label, + code: item.value, + value: item.value, + label: item.label, + } + if (item.children && item.children.length > 0) { + result.children = convert(item.children) } - return item + return result }) } - return buildTree('0') + return convert(areaList) }, handleItem(e: any) { const { code, name } = e.currentTarget.dataset diff --git a/src/doctor/pages/home/index.ts b/src/doctor/pages/home/index.ts index 2aad9ae..1daa29b 100644 --- a/src/doctor/pages/home/index.ts +++ b/src/doctor/pages/home/index.ts @@ -18,13 +18,15 @@ Page({ // 待处理患者数 pendingCount: 0, + jumpPendingCount: 0, + enrollPendingCount: 0, - // 统计数据 + // 累计统计数据 invitePatientCount: 0, jumpPatientCount: 0, enrollPatientCount: 0, - // 适应症统计 + // 累计适应症统计 indicationStats: [] as Array<{ indicationId: number indicationName: string @@ -33,8 +35,27 @@ Page({ enrollPatientCount: number }>, + // 日/月度统计数据 + dailyInvitePatientCount: 0, + dailyJumpPatientCount: 0, + dailyEnrollPatientCount: 0, + + // 日/月度适应症统计 + dailyIndicationStats: [] as Array<{ + indicationId: number + indicationName: string + invitePatientCount: number + jumpPatientCount: number + enrollPatientCount: number + }>, + // 图表数据 - chartData: [] as Array<{ date: string; count: number }>, + chartData: [] as Array<{ + date: string + invitePatientCount: number + jumpPatientCount: number + enrollPatientCount: number + }>, // 日期范围 - 邀约患者统计卡片(单日) startDate: '', @@ -131,7 +152,8 @@ Page({ // 获取其他数据 this.getPendingCount() - this.getStatistics() + this.getTotalStatistics() + this.getDailyStatistics() this.getPatientChart() }) }, @@ -158,7 +180,8 @@ Page({ }) // 重新加载数据 this.getPendingCount() - this.getStatistics() + this.getTotalStatistics() + this.getDailyStatistics() this.getPatientChart() wx.showToast({ title: '切换成功', @@ -182,19 +205,23 @@ Page({ }) .then((res: any) => { this.setData({ - pendingCount: res.count || 0, + pendingCount: res.totalPendingCount || 0, + jumpPendingCount: res.jumpPendingCount || 0, + enrollPendingCount: res.enrollPendingCount || 0, }) }) .catch(() => { - // 接口失败时使用模拟数据 + // 接口失败时使用默认值 this.setData({ - pendingCount: 12, + pendingCount: 0, + jumpPendingCount: 0, + enrollPendingCount: 0, }) }) }, - // 获取统计数据看板 - getStatistics() { + // 获取累计统计数据看板 + getTotalStatistics() { wx.ajax({ method: 'GET', url: '/app/pharmacist/pharmacist/statistics', @@ -208,6 +235,31 @@ Page({ }) }, + // 获取日/月度统计数据看板 + getDailyStatistics() { + // 根据统计类型格式化日期 + const statDate = + this.data.statType === 'month' + ? this.data.startMonth // YYYY-MM + : this.data.startDate // YYYY-MM-DD + + wx.ajax({ + method: 'GET', + url: '/app/pharmacist/pharmacist/patient-statistics', + data: { + statDate, + type: this.data.statType, + }, + }).then((res: any) => { + this.setData({ + dailyInvitePatientCount: res.invitePatientCount || 0, + dailyJumpPatientCount: res.jumpPatientCount || 0, + dailyEnrollPatientCount: res.enrollPatientCount || 0, + dailyIndicationStats: res.indicationStats || [], + }) + }) + }, + // 获取邀约患者统计图表(使用 chart 的日期范围) getPatientChart() { // 根据统计类型格式化日期 @@ -232,8 +284,10 @@ Page({ const list = res || [] // 转换为图表需要的格式 const chartData = list.map((item: any) => ({ - date: item.statDate || item.date, - count: item.invitePatientCount || item.count || 0, + date: item.statDate, + invitePatientCount: item.invitePatientCount || 0, + jumpPatientCount: item.jumpPatientCount || 0, + enrollPatientCount: item.enrollPatientCount || 0, })) this.setData({ chartData, @@ -284,7 +338,7 @@ Page({ chartStartMonth, chartEndMonth, }) - this.getStatistics() + this.getDailyStatistics() this.getPatientChart() }, @@ -301,8 +355,8 @@ Page({ startDate: fullDate, startMonth: monthValue, }) - // 重新加载统计数据 - this.getStatistics() + // 重新加载日/月度统计数据 + this.getDailyStatistics() }, // 切换到上一天/上月 @@ -327,8 +381,8 @@ Page({ } this.setData({ startDate, startMonth }) - // 重新加载统计数据 - this.getStatistics() + // 重新加载日/月度统计数据 + this.getDailyStatistics() }, // 切换到下一天/下月 @@ -375,8 +429,8 @@ Page({ } this.setData({ startDate, startMonth }) - // 重新加载统计数据 - this.getStatistics() + // 重新加载日/月度统计数据 + this.getDailyStatistics() }, // 图表日期选择变化(开始日期) @@ -442,10 +496,14 @@ Page({ }) canvas.setChart(chart) const x: string[] = [] - const y1: string[] = [] + const y1: number[] = [] + const y2: number[] = [] + const y3: number[] = [] list.forEach((item) => { - x.push(item.date || item.StatMonth) - y1.push(item.count || item.MonthInvitePCount) + x.push(item.date) + y1.push(item.enrollPatientCount) + y2.push(item.jumpPatientCount) + y3.push(item.invitePatientCount) }) const option = { @@ -519,7 +577,7 @@ Page({ stack: 'a', color: '#FF8A4C', barWidth: 12, - data: y1, + data: y2, }, { name: '邀约患者数', @@ -528,7 +586,7 @@ Page({ width: 4, color: '#FFA64D', barWidth: 12, - data: y1, + data: y3, }, ], dataZoom: { @@ -553,8 +611,9 @@ Page({ }, handleStat() { + const { chartStartDate, chartEndDate, statType } = this.data wx.navigateTo({ - url: '/doctor/pages/stat/index', + url: `/doctor/pages/stat/index?startDate=${chartStartDate}&endDate=${chartEndDate}&type=${statType}`, }) }, diff --git a/src/doctor/pages/home/index.wxml b/src/doctor/pages/home/index.wxml index 72caef8..072f068 100644 --- a/src/doctor/pages/home/index.wxml +++ b/src/doctor/pages/home/index.wxml @@ -38,13 +38,13 @@ 跳转证明\n待上传 - 0 + {{jumpPendingCount}} 入组证明\n待上传 - 0 + {{enrollPendingCount}} @@ -53,7 +53,7 @@ 累计邀约 - + {{fold1?'展开':'收起'}} @@ -107,7 +107,7 @@ 邀约患者统计 - + {{fold2?'展开':'收起'}} @@ -124,21 +124,21 @@ 邀约患者数 - {{invitePatientCount}} + {{dailyInvitePatientCount}} 跳转患者数 - {{jumpPatientCount}} + {{dailyJumpPatientCount}} 入组患者数 - {{enrollPatientCount}} + {{dailyEnrollPatientCount}} - + {{item.indicationName}} {{item.invitePatientCount}} diff --git a/src/doctor/pages/loginForm/index.scss b/src/doctor/pages/loginForm/index.scss index 614dfbe..d14dcf5 100644 --- a/src/doctor/pages/loginForm/index.scss +++ b/src/doctor/pages/loginForm/index.scss @@ -56,7 +56,7 @@ page { font-size: 32rpx; color: #342317; } - .picker, + .picker-wrap, .wrap { flex: 1; display: flex; diff --git a/src/doctor/pages/loginForm/index.ts b/src/doctor/pages/loginForm/index.ts index d27f8fa..8fddce6 100644 --- a/src/doctor/pages/loginForm/index.ts +++ b/src/doctor/pages/loginForm/index.ts @@ -7,6 +7,13 @@ Page({ pharmacyId: '', pharmacyName: '', + // 项目选择 + projectId: '', + projectName: '', + projectList: [] as Array<{ projectId: number; projectName: string }>, + projectIndex: 0, + projectDisabled: false, // 是否禁用项目选择(扫码进入时) + // 药店选择弹窗 show: false, @@ -36,7 +43,65 @@ Page({ }, }, onLoad() { - // 页面加载 + // 如果通过扫码进入,存在项目ID,则回显并禁用选择 + const projectId = app.globalData.projectId + if (projectId) { + this.setData({ + projectId, + projectDisabled: true, + }) + } + + // 获取项目列表 + this.getProjectList() + }, + + // 获取项目列表 + getProjectList() { + wx.ajax({ + method: 'GET', + url: '/app/common/common/project-list', + }).then((res: any) => { + const list = res.data || res || [] + + // 转换数据格式:接口返回 id/name,组件使用 projectId/projectName + const projectList = list.map((item: any) => ({ + projectId: item.id, + projectName: item.name, + })) + + // 如果有预设的项目ID(扫码进入),找到对应索引并回显 + let projectIndex = 0 + let projectId = '' + let projectName = '' + + if (this.data.projectId) { + const index = projectList.findIndex((item: any) => String(item.projectId) === String(this.data.projectId)) + if (index !== -1) { + projectIndex = index + projectId = projectList[index].projectId + projectName = projectList[index].projectName + } + } + + this.setData({ + projectList, + projectIndex, + projectId, + projectName, + }) + }) + }, + + // 项目选择变化 + onProjectChange(e: WechatMiniprogram.CustomEvent) { + const index = e.detail.value + const project = this.data.projectList[index] + this.setData({ + projectIndex: index, + projectId: project?.projectId || '', + projectName: project?.projectName || '', + }) }, // 输入姓名 handleNameInput(e: WechatMiniprogram.CustomEvent) { @@ -88,13 +153,24 @@ Page({ const currentPage = this.data.page const total = res.total || 0 const pages = Math.ceil(total / this.data.pageSize) - const otherPharmacy = { - id: '-1', - name: '其他药店', - address: '没有找到所在药店', + + // 判断是否有筛选条件(关键词或省市区) + const hasFilter = this.data.keyword || this.data.provinceId || this.data.cityId || this.data.districtId + + // 只有在没有筛选条件且是最后一页时,才添加"其他药店" + const isLastPage = currentPage >= pages + const newList = [...this.data.pharmacyList, ...list] + if (hasFilter && isLastPage) { + const otherPharmacy = { + id: '-1', + name: '其他药店', + address: '没有找到所在药店', + } + newList.push(otherPharmacy) } + this.setData({ - pharmacyList: [...this.data.pharmacyList, ...list, otherPharmacy], + pharmacyList: newList, total, page: currentPage + 1, hasMore: currentPage < pages, @@ -161,7 +237,7 @@ Page({ }, // 提交注册 handleSubmit() { - const { name, pharmacyId } = this.data + const { name, pharmacyId, projectId, projectDisabled } = this.data if (!name.trim()) { wx.showToast({ @@ -179,6 +255,14 @@ Page({ return } + if (!projectId) { + wx.showToast({ + title: '请选择项目', + icon: 'none', + }) + return + } + wx.showLoading({ title: '提交中...' }) wx.ajax({ @@ -187,6 +271,7 @@ Page({ data: { name: name.trim(), pharmacyId, + projectId, }, }) .then((res: any) => { diff --git a/src/doctor/pages/loginForm/index.wxml b/src/doctor/pages/loginForm/index.wxml index 11209cf..388b973 100644 --- a/src/doctor/pages/loginForm/index.wxml +++ b/src/doctor/pages/loginForm/index.wxml @@ -1,3 +1,5 @@ + + 1 @@ -13,7 +15,14 @@ 您的姓名 - + @@ -30,6 +39,30 @@ + + 选择项目 + + + + + + + diff --git a/src/doctor/pages/my/index.ts b/src/doctor/pages/my/index.ts index 247f6b9..d3f1a70 100644 --- a/src/doctor/pages/my/index.ts +++ b/src/doctor/pages/my/index.ts @@ -11,6 +11,9 @@ Page({ pharmacyName: '', }, + // 当前项目信息 + currentProjectName: '', + // 邀约人列表 inviterList: [] as Array<{ promoterName: string @@ -18,10 +21,29 @@ Page({ projectName: string }>, }, - onLoad() { + onShow() { // 药店端我的页面,仅允许药店人员访问 app.waitLogin({ types: [4] }).then(() => { this.getProfile() + this.getProjectList() + }) + }, + + // 获取项目列表 + getProjectList() { + wx.ajax({ + method: 'GET', + url: '/app/pharmacist/pharmacist/project-list', + }).then((res: any) => { + const projectList = res.list || [] + const currentProjectId = res.currentProjectId || (projectList[0]?.projectId || 0) + const currentProject = projectList.find((item: any) => item.projectId === currentProjectId) || projectList[0] + + this.setData({ + currentProjectName: currentProject?.projectName || '', + }) + + // 获取邀约人信息 this.getInviterInfo() }) }, @@ -49,7 +71,12 @@ Page({ url: '/app/pharmacist/pharmacist/inviter-info', }).then((res: any) => { const list = res.list || res || [] - const inviterList = list.map((item: any) => ({ + const currentProjectName = this.data.currentProjectName + + // 过滤出当前项目的邀约人 + const filteredList = list.filter((item: any) => item.projectName === currentProjectName) + + const inviterList = filteredList.map((item: any) => ({ promoterName: item.promoterName || '', promoterPhone: item.promoterPhone || '', projectName: item.projectName || '', diff --git a/src/doctor/pages/patientList/index.wxml b/src/doctor/pages/patientList/index.wxml index e6e85be..56aed39 100644 --- a/src/doctor/pages/patientList/index.wxml +++ b/src/doctor/pages/patientList/index.wxml @@ -1,3 +1,4 @@ + - + 时间类型: - {{timeType === 0 ? '跳转时间' : '入组时间'}} + {{timeType === 0 ? '跳转' : '入组'}} diff --git a/src/doctor/pages/stat/index.ts b/src/doctor/pages/stat/index.ts index 743d2c4..07308a2 100644 --- a/src/doctor/pages/stat/index.ts +++ b/src/doctor/pages/stat/index.ts @@ -37,22 +37,27 @@ Page({ }, }, - onLoad() { + onLoad(options: any) { // 药店端统计页面,仅允许药店人员访问 app.waitLogin({ types: [4] }).then(() => { - this.initDate() + this.initDate(options) }) }, // 初始化日期 - initDate() { + initDate(options: any) { const today = this.formatDate(new Date()) - const startDate = this.formatDate(new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)) + + // 从URL参数获取日期,如果没有则使用默认值 + const startDate = options?.startDate || this.formatDate(new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)) + const endDate = options?.endDate || today + const statType = options?.type || 'day' this.setData({ startDate, - endDate: today, + endDate, today, + statType, }) this.getStatisticsList() }, diff --git a/src/ground/pages/home/index.ts b/src/ground/pages/home/index.ts index 9d8770e..23ba45e 100644 --- a/src/ground/pages/home/index.ts +++ b/src/ground/pages/home/index.ts @@ -849,8 +849,9 @@ Page({ }, handleInfo() { + const { chart1StartDate, chart1EndDate, statType } = this.data wx.navigateTo({ - url: '/ground/pages/stat/index', + url: `/ground/pages/stat/index?startDate=${chart1StartDate}&endDate=${chart1EndDate}&type=${statType}`, }) }, diff --git a/src/ground/pages/my/index.ts b/src/ground/pages/my/index.ts index b526e83..0b49876 100644 --- a/src/ground/pages/my/index.ts +++ b/src/ground/pages/my/index.ts @@ -7,16 +7,12 @@ Page({ promoterAvatar: '', promoterPhone: '', }, - onLoad() { + onShow() { // 地推端我的页面,仅允许地推人员访问 app.waitLogin({ types: [3] }).then(() => { this.getUserInfo() }) }, - onShow() { - // 页面显示时刷新用户信息 - this.getUserInfo() - }, // 获取个人信息 getUserInfo() { wx.ajax({ diff --git a/src/ground/pages/pharmacist/index.ts b/src/ground/pages/pharmacist/index.ts index 8854f8e..109b5f8 100644 --- a/src/ground/pages/pharmacist/index.ts +++ b/src/ground/pages/pharmacist/index.ts @@ -218,8 +218,9 @@ Page({ // 查看详情 handleInfo(e: WechatMiniprogram.CustomEvent) { const { id } = e.currentTarget.dataset + const { startDate, endDate } = this.data wx.navigateTo({ - url: `/ground/pages/stat/index?id=${id}`, + url: `/ground/pages/stat/index?id=${id}&startDate=${startDate}&endDate=${endDate}`, }) }, }) diff --git a/src/ground/pages/stat/index.ts b/src/ground/pages/stat/index.ts index b83e019..2ab5b0d 100644 --- a/src/ground/pages/stat/index.ts +++ b/src/ground/pages/stat/index.ts @@ -2,6 +2,9 @@ const app = getApp() Page({ data: { + // 药师ID(从药师列表进入时) + pharmacistId: 0, + // 时间筛选 startDate: '', endDate: '', @@ -24,26 +27,45 @@ Page({ pages: 0, }, }, - onLoad() { + onLoad(options: any) { // 地推端统计页面,仅允许地推人员访问 app.waitLogin({ types: [3] }).then(() => { - // 设置默认时间范围(2026年3月至今) - this.setDefaultDateRange() + // 设置参数 + this.setOptions(options) // 获取列表数据(包含顶部统计数据) this.getList(true) }) }, - // 设置默认时间范围(2026年3月至今) - setDefaultDateRange() { - const endDate = new Date() - const startDate = new Date('2026-03-01') - - this.setData({ - startDate: this.formatDate(startDate), - endDate: this.formatDate(endDate), - }) + + // 设置页面参数 + setOptions(options: any) { + // 设置时间范围 + if (options?.startDate && options?.endDate) { + this.setData({ + startDate: options.startDate, + endDate: options.endDate, + type: options.type || 'day', + }) + } else { + // 默认时间范围(2026年3月至今) + const endDate = new Date() + const startDate = new Date('2026-03-01') + + this.setData({ + startDate: this.formatDate(startDate), + endDate: this.formatDate(endDate), + }) + } + + // 设置药师ID(从药师列表进入) + if (options?.id) { + this.setData({ + pharmacistId: Number.parseInt(options.id), + }) + } }, + // 格式化日期 formatDate(date: Date): string { const year = date.getFullYear() @@ -53,7 +75,7 @@ Page({ }, // 获取列表数据(包含顶部统计数据) getList(reset = false) { - const { startDate, endDate, type, page, pageSize, pagination } = this.data + const { pharmacistId, startDate, endDate, type, page, pageSize, pagination } = this.data // 如果是重置(如筛选条件变化),先重置状态 if (reset) { @@ -70,6 +92,25 @@ Page({ const currentPage = reset ? 1 : page + // 根据是否有 pharmacistId 决定调用哪个接口 + if (pharmacistId > 0) { + // 从药师列表进入 - 查看指定药师的统计 + this.getPharmacistStatistics(pharmacistId, startDate, endDate, currentPage, pageSize, reset) + } else { + // 从首页进入 - 查看全部统计 + this.getPatientStatisticsList(startDate, endDate, type, currentPage, pageSize, reset) + } + }, + + // 获取患者统计列表(从首页进入时使用) + getPatientStatisticsList( + startDate: string, + endDate: string, + type: string, + page: number, + pageSize: number, + reset: boolean + ) { wx.ajax({ method: 'GET', url: '/app/promoter/promoter/patient-statistics-list', @@ -77,7 +118,7 @@ Page({ startDate, endDate, type, - page: currentPage, + page, pageSize, }, }).then((res: any) => { @@ -109,10 +150,68 @@ Page({ enrollPatientCount: summary.enrollPatientCount || 0, // 列表数据 list, - page: currentPage + 1, + page: page + 1, + pagination: { + count: total, + page, + pages, + }, + }) + }) + }, + + // 获取药师统计(从药师列表进入时使用) + getPharmacistStatistics( + pharmacistId: number, + startDate: string, + endDate: string, + page: number, + pageSize: number, + reset: boolean + ) { + wx.ajax({ + method: 'GET', + url: '/app/promoter/promoter/pharmacist-statistics', + data: { + pharmacistId, + startDate, + endDate, + page, + pageSize, + }, + }).then((res: any) => { + const summary = res.summary || {} + const newList = res.list || [] + const total = res.total || 0 + + // 转换数据格式 + const formattedList = newList.map((item: any) => ({ + date: item.statDate, + inviteCount: item.invitePatientCount || 0, + jumpCount: item.jumpPatientCount || 0, + enrollCount: item.enrollPatientCount || 0, + indicationStats: (item.indicationStats || []).map((ind: any) => ({ + indicationName: ind.indicationName, + inviteCount: ind.invitePatientCount || 0, + jumpCount: ind.jumpPatientCount || 0, + enrollCount: ind.enrollPatientCount || 0, + })), + })) + + const list = reset ? formattedList : [...this.data.list, ...formattedList] + const pages = Math.ceil(total / pageSize) + + this.setData({ + // 顶部统计数据从 summary 获取 + invitePatientCount: summary.invitePatientCount || 0, + jumpPatientCount: summary.jumpPatientCount || 0, + enrollPatientCount: summary.enrollPatientCount || 0, + // 列表数据 + list, + page: page + 1, pagination: { count: total, - page: currentPage, + page, pages, }, }) diff --git a/src/pages/index/index.wxml b/src/pages/index/index.wxml index 0000ea9..9aa0e68 100644 --- a/src/pages/index/index.wxml +++ b/src/pages/index/index.wxml @@ -1,3 +1,4 @@ + {{currentProjectName}} diff --git a/src/pages/tourists/index.scss b/src/pages/tourists/index.scss index a5d2a3a..8c8961a 100644 --- a/src/pages/tourists/index.scss +++ b/src/pages/tourists/index.scss @@ -9,6 +9,38 @@ page { } .page { + .user { + margin-bottom: -40rpx; + padding: 40rpx 40rpx 0; + display: flex; + align-items: center; + gap: 24rpx; + .avatar-wrapper { + margin: 0; + width: 104rpx; + height: 104rpx; + border-radius: 50%; + border: none; + outline: none; + background-color: transparent; + &::after { + border: none; + } + .avatar { + width: 100%; + height: 100%; + border-radius: 50%; + } + } + .input { + font-size: 36rpx; + color: #ffffff; + } + .input-placeholder { + font-size: 36rpx; + color: #ffffff; + } + } .page-banner { width: 100vw; height: 1108rpx; diff --git a/src/pages/tourists/index.wxml b/src/pages/tourists/index.wxml index 5fc59a8..dcd8291 100644 --- a/src/pages/tourists/index.wxml +++ b/src/pages/tourists/index.wxml @@ -2,7 +2,16 @@ 华观健康 - + + + + + 我是药店工作人员 diff --git a/src/pages/work/index.wxml b/src/pages/work/index.wxml index 8e727c3..5afaa54 100644 --- a/src/pages/work/index.wxml +++ b/src/pages/work/index.wxml @@ -1,3 +1,4 @@ +