Browse Source

feat(ground): 添加项目切换功能并优化统计页面

- 在药师页面添加项目切换功能
- 重构统计页面数据获取逻辑,分离累计数据和日期筛选数据
- 为不同图表添加独立的日期范围控制
- 优化页面数据展示逻辑,添加空状态处理
- 修复分页组件显示条件
master
kola-web 1 week ago
parent
commit
14b9ee4fc0
  1. 2
      src/doctor/pages/articleList/index.wxml
  2. 3
      src/doctor/pages/home/index.json
  3. 4
      src/doctor/pages/home/index.scss
  4. 3
      src/doctor/pages/home/index.wxml
  5. 2
      src/doctor/pages/loginForm/index.wxml
  6. 2
      src/doctor/pages/patientList/index.ts
  7. 2
      src/doctor/pages/patientList/index.wxml
  8. 8
      src/doctor/pages/stat/index.wxml
  9. 3
      src/ground/pages/home/index.json
  10. 236
      src/ground/pages/home/index.ts
  11. 42
      src/ground/pages/home/index.wxml
  12. 1
      src/ground/pages/invite/index.wxml
  13. 72
      src/ground/pages/pharmacist/index.ts
  14. 14
      src/ground/pages/pharmacist/index.wxml
  15. 132
      src/ground/pages/stat/index.ts
  16. 9
      src/ground/pages/stat/index.wxml

2
src/doctor/pages/articleList/index.wxml

@ -41,7 +41,7 @@
</view> </view>
</view> </view>
<!-- 分页组件 --> <!-- 分页组件 -->
<pagination pagination="{{pagination}}" wx:if="{{!loading || articleList.length > 0}}"></pagination> <pagination pagination="{{pagination}}"></pagination>
</view> </view>
<doctor-tab-bar active="{{ 2 }}"></doctor-tab-bar> <doctor-tab-bar active="{{ 2 }}"></doctor-tab-bar>

3
src/doctor/pages/home/index.json

@ -4,6 +4,7 @@
"usingComponents": { "usingComponents": {
"navbar": "../../../components/navbar/index", "navbar": "../../../components/navbar/index",
"doctor-tab-bar": "/doctor/components/doctor-tab-bar/index", "doctor-tab-bar": "/doctor/components/doctor-tab-bar/index",
"ec-canvas": "/components/ec-canvas/ec-canvas" "ec-canvas": "/components/ec-canvas/ec-canvas",
"van-empty": "@vant/weapp/empty/index"
} }
} }

4
src/doctor/pages/home/index.scss

@ -412,6 +412,10 @@ page {
width: 100%; width: 100%;
height: 546rpx; height: 546rpx;
box-sizing: border-box; box-sizing: border-box;
.hide {
display: none;
}
} }
.more { .more {
padding: 20rpx 0 0; padding: 20rpx 0 0;

3
src/doctor/pages/home/index.wxml

@ -172,7 +172,8 @@
</picker> </picker>
</view> </view>
<view class="chart-container"> <view class="chart-container">
<ec-canvas id="chart1_1" canvas-id="mychart-bar" ec="{{ ec }}"></ec-canvas> <ec-canvas class="{{chartData.length === 0 ? 'hide' : ''}}" id="chart1_1" canvas-id="mychart-bar" ec="{{ ec }}"></ec-canvas>
<van-empty class="{{chartData.length > 0 ? 'hide' : ''}}" description="暂无数据" />
</view> </view>
<view class="more" bind:tap="handleStat"> <view class="more" bind:tap="handleStat">
查看明细 查看明细

2
src/doctor/pages/loginForm/index.wxml

@ -75,7 +75,7 @@
<view class="name">{{item.name}}</view> <view class="name">{{item.name}}</view>
<view class="site">{{item.provinceName}}{{item.cityName}}{{item.districtName}}{{item.address}}</view> <view class="site">{{item.provinceName}}{{item.cityName}}{{item.districtName}}{{item.address}}</view>
</view> </view>
<pagination pagination="{{pagination}}" wx:if="{{pharmacyList.length > 0}}" /> <pagination pagination="{{pagination}}" />
</scroll-view> </scroll-view>
</view> </view>
</van-popup> </van-popup>

2
src/doctor/pages/patientList/index.ts

@ -267,7 +267,7 @@ Page({
}) })
}, },
// 弹窗确认 // 弹窗确认
handlePopupOk(e: WechatMiniprogram.CustomEvent) { handlePopupOk() {
this.setData({ this.setData({
popupShow: false, popupShow: false,
}) })

2
src/doctor/pages/patientList/index.wxml

@ -139,7 +139,7 @@
</view> </view>
</view> </view>
<!-- 分页组件 --> <!-- 分页组件 -->
<pagination pagination="{{pagination}}" wx:if="{{!loading || patientList.length > 0}}"></pagination> <pagination pagination="{{pagination}}"></pagination>
</view> </view>
</view> </view>

8
src/doctor/pages/stat/index.wxml

@ -8,16 +8,16 @@
<view class="page" style="padding-top: {{pageTop}}px;"> <view class="page" style="padding-top: {{pageTop}}px;">
<view class="page-header" style="top:{{pageTop}}px"> <view class="page-header" style="top:{{pageTop}}px">
<view class="chart-range"> <view class="chart-range">
<picker class="picker" mode="date" end="{{end}}"> <picker class="picker" mode="date" value="{{startDate}}" end="{{endDate}}" bindchange="handleStartDateChange">
<view class="p-content"> <view class="p-content">
<view class="content">2025/02/26</view> <view class="content">{{startDate}}</view>
<image class="icon" src="{{imageUrl}}icon13.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon13.png?t={{Timestamp}}"></image>
</view> </view>
</picker> </picker>
<view class="line"></view> <view class="line"></view>
<picker class="picker" mode="date" start="{{satrt}}"> <picker class="picker" mode="date" value="{{endDate}}" start="{{startDate}}" bindchange="handleEndDateChange">
<view class="p-content"> <view class="p-content">
<view class="content">2025/02/26</view> <view class="content">{{endDate}}</view>
<image class="icon" src="{{imageUrl}}icon13.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon13.png?t={{Timestamp}}"></image>
</view> </view>
</picker> </picker>

3
src/ground/pages/home/index.json

@ -4,6 +4,7 @@
"usingComponents": { "usingComponents": {
"navbar": "../../../components/navbar/index", "navbar": "../../../components/navbar/index",
"ground-tab-bar": "/ground/components/ground-tab-bar/index", "ground-tab-bar": "/ground/components/ground-tab-bar/index",
"ec-canvas": "/components/ec-canvas/ec-canvas" "ec-canvas": "/components/ec-canvas/ec-canvas",
"van-empty": "@vant/weapp/empty/index"
} }
} }

236
src/ground/pages/home/index.ts

@ -10,9 +10,21 @@ Page({
promoterAvatar: '', promoterAvatar: '',
label: '邀约专员', label: '邀约专员',
// 统计数据 // 累计统计数据(来自 getStatistics)
invitePharmacyCount: 0, totalInvitePharmacyCount: 0,
invitePharmacistCount: 0, totalInvitePharmacistCount: 0,
totalInvitePatientCount: 0,
totalJumpPatientCount: 0,
totalEnrollPatientCount: 0,
totalIndicationStats: [] as Array<{
indicationId: number
indicationName: string
invitePatientCount: number
jumpPatientCount: number
enrollPatientCount: number
}>,
// 日期筛选统计数据(来自 getPatientStatistics)
invitePatientCount: 0, invitePatientCount: 0,
jumpPatientCount: 0, jumpPatientCount: 0,
enrollPatientCount: 0, enrollPatientCount: 0,
@ -29,11 +41,22 @@ Page({
pharmacistChartData: [] as Array<{ date: string, count: number }>, pharmacistChartData: [] as Array<{ date: string, count: number }>,
pharmacyChartData: [] as Array<{ date: string, count: number }>, pharmacyChartData: [] as Array<{ date: string, count: number }>,
// 日期范围 // 日期范围 - 邀约患者统计卡片(单日)
startDate: '', startDate: '',
endDate: '',
today: '', today: '',
// 日期范围 - 图表1(邀约患者统计)
chart1StartDate: '',
chart1EndDate: '',
// 日期范围 - 图表2(邀约药师统计)
chart2StartDate: '',
chart2EndDate: '',
// 日期范围 - 图表3(邀约药店统计)
chart3StartDate: '',
chart3EndDate: '',
// 统计类型: day-日统计, month-月统计 // 统计类型: day-日统计, month-月统计
statType: 'day', statType: 'day',
@ -56,14 +79,20 @@ Page({
this.getProjectList() this.getProjectList()
}) })
// 初始化日期范围为最近30天 // 初始化日期
const today = this.formatDate(new Date()) const today = this.formatDate(new Date())
const endDate = today const defaultStartDate = this.formatDate(new Date(Date.now() - 30 * 24 * 60 * 60 * 1000))
const startDate = this.formatDate(new Date(Date.now() - 30 * 24 * 60 * 60 * 1000))
this.setData({ this.setData({
today, today,
startDate, // 邀约患者统计卡片(单日)- 默认为今天
endDate, startDate: today,
// 图表1-3(日期范围)- 默认为最近30天
chart1StartDate: defaultStartDate,
chart1EndDate: today,
chart2StartDate: defaultStartDate,
chart2EndDate: today,
chart3StartDate: defaultStartDate,
chart3EndDate: today,
}) })
}, },
@ -108,6 +137,7 @@ Page({
// 获取统计数据 // 获取统计数据
this.getStatistics() this.getStatistics()
this.getPatientStatistics()
this.getPatientChart() this.getPatientChart()
this.getPharmacistChart() this.getPharmacistChart()
this.getPharmacyChart() this.getPharmacyChart()
@ -119,6 +149,15 @@ Page({
const index = e.detail.value const index = e.detail.value
const project = this.data.projectList[index] const project = this.data.projectList[index]
if (project && project.projectId !== this.data.currentProjectId) { if (project && project.projectId !== this.data.currentProjectId) {
// 先调用切换项目接口
wx.ajax({
method: 'POST',
url: '/app/promoter/promoter/switch-project',
data: {
projectId: project.projectId,
},
}).then(() => {
// 切换成功后更新页面数据
this.setData({ this.setData({
currentProjectId: project.projectId, currentProjectId: project.projectId,
currentProjectName: project.projectName, currentProjectName: project.projectName,
@ -126,26 +165,52 @@ Page({
}) })
// 重新加载数据 // 重新加载数据
this.getStatistics() this.getStatistics()
this.getPatientStatistics()
this.getPatientChart() this.getPatientChart()
this.getPharmacistChart() this.getPharmacistChart()
this.getPharmacyChart() this.getPharmacyChart()
wx.showToast({
title: '切换成功',
icon: 'success',
})
}).catch(() => {
wx.showToast({
title: '切换失败',
icon: 'none',
})
})
} }
}, },
// 获取统计数据看板 // 获取统计数据看板(累计数据)
getStatistics() { getStatistics() {
const data: any = {}
if (this.data.currentProjectId) {
data.projectId = this.data.currentProjectId
}
wx.ajax({ wx.ajax({
method: 'GET', method: 'GET',
url: '/app/promoter/promoter/statistics', url: '/app/promoter/promoter/statistics',
data,
}).then((res: any) => { }).then((res: any) => {
this.setData({ this.setData({
invitePharmacyCount: res.invitePharmacyCount || 0, // 累计数据
invitePharmacistCount: res.invitePharmacistCount || 0, totalInvitePharmacyCount: res.invitePharmacyCount || 0,
totalInvitePharmacistCount: res.invitePharmacistCount || 0,
totalInvitePatientCount: res.invitePatientCount || 0,
totalJumpPatientCount: res.jumpPatientCount || 0,
totalEnrollPatientCount: res.enrollPatientCount || 0,
totalIndicationStats: res.indicationStats || [],
})
})
},
// 获取日/月度邀约患者统计数据(按日期筛选)
getPatientStatistics() {
wx.ajax({
method: 'GET',
url: '/app/promoter/promoter/patient-statistics',
data: {
statDate: this.data.startDate,
type: this.data.statType,
},
}).then((res: any) => {
this.setData({
invitePatientCount: res.invitePatientCount || 0, invitePatientCount: res.invitePatientCount || 0,
jumpPatientCount: res.jumpPatientCount || 0, jumpPatientCount: res.jumpPatientCount || 0,
enrollPatientCount: res.enrollPatientCount || 0, enrollPatientCount: res.enrollPatientCount || 0,
@ -154,34 +219,38 @@ Page({
}) })
}, },
// 获取邀约患者统计图表 // 获取邀约患者统计图表(使用 chart1 的日期)
getPatientChart() { getPatientChart() {
const data: any = {
type: this.data.statType,
startDate: this.data.startDate,
endDate: this.data.endDate,
}
if (this.data.currentProjectId) {
data.projectId = this.data.currentProjectId
}
wx.ajax({ wx.ajax({
method: 'GET', method: 'GET',
url: '/app/promoter/promoter/patient-chart', url: '/app/promoter/promoter/patient-chart',
data, data: {
type: this.data.statType,
startDate: this.data.chart1StartDate,
endDate: this.data.chart1EndDate,
},
}).then((list: any) => { }).then((list: any) => {
// 新接口返回的数据格式包含 invitePatientCount, jumpPatientCount, enrollPatientCount, indicationStats
// 转换为图表需要的格式
const chartData = (list || []).map((item: any) => ({
date: item.statDate,
inviteCount: item.invitePatientCount || 0,
jumpCount: item.jumpPatientCount || 0,
enrollCount: item.enrollPatientCount || 0,
}))
this.setData({ this.setData({
chartData: list || [], chartData,
}) })
this.initChartBar(list || []) this.initChartBar(chartData)
}) })
}, },
// 获取邀约药师统计图表 // 获取邀约药师统计图表(使用 chart2 的日期)
getPharmacistChart() { getPharmacistChart() {
const data: any = { const data: any = {
type: this.data.statType, type: this.data.statType,
startDate: this.data.startDate, startDate: this.data.chart2StartDate,
endDate: this.data.endDate, endDate: this.data.chart2EndDate,
} }
if (this.data.currentProjectId) { if (this.data.currentProjectId) {
data.projectId = this.data.currentProjectId data.projectId = this.data.currentProjectId
@ -198,12 +267,12 @@ Page({
}) })
}, },
// 获取邀约药店统计图表 // 获取邀约药店统计图表(使用 chart3 的日期)
getPharmacyChart() { getPharmacyChart() {
const data: any = { const data: any = {
type: this.data.statType, type: this.data.statType,
startDate: this.data.startDate, startDate: this.data.chart3StartDate,
endDate: this.data.endDate, endDate: this.data.chart3EndDate,
} }
if (this.data.currentProjectId) { if (this.data.currentProjectId) {
data.projectId = this.data.currentProjectId data.projectId = this.data.currentProjectId
@ -226,25 +295,39 @@ Page({
this.setData({ this.setData({
statType: type, statType: type,
}) })
this.getPatientStatistics()
this.getPatientChart() this.getPatientChart()
this.getPharmacistChart() this.getPharmacistChart()
this.getPharmacyChart() this.getPharmacyChart()
}, },
// 日期选择变化 // 日期选择变化(邀约患者统计卡片 - 单日选择)
onDateChange(e: WechatMiniprogram.CustomEvent) { onDateChange(e: WechatMiniprogram.CustomEvent) {
const value = e.detail.value
this.setData({
startDate: value,
})
// 重新加载统计数据
this.getPatientStatistics()
},
// 图表1日期选择变化
onChart1DateChange(e: WechatMiniprogram.CustomEvent) {
const { field } = e.currentTarget.dataset const { field } = e.currentTarget.dataset
const value = e.detail.value const value = e.detail.value
const startDate = this.data.chart1StartDate
const endDate = this.data.chart1EndDate
// 验证日期范围 // 验证日期范围
if (field === 'startDate' && this.data.endDate && value > this.data.endDate) { if (field === 'startDate' && endDate && value > endDate) {
wx.showToast({ wx.showToast({
title: '开始时间不能大于结束时间', title: '开始时间不能大于结束时间',
icon: 'none', icon: 'none',
}) })
return return
} }
if (field === 'endDate' && this.data.startDate && value < this.data.startDate) { if (field === 'endDate' && startDate && value < startDate) {
wx.showToast({ wx.showToast({
title: '结束时间不能小于开始时间', title: '结束时间不能小于开始时间',
icon: 'none', icon: 'none',
@ -253,28 +336,81 @@ Page({
} }
this.setData({ this.setData({
[field]: value, [field === 'startDate' ? 'chart1StartDate' : 'chart1EndDate']: value,
}) })
// 重新加载图表数据
this.getPatientChart() this.getPatientChart()
},
// 图表2日期选择变化
onChart2DateChange(e: WechatMiniprogram.CustomEvent) {
const { field } = e.currentTarget.dataset
const value = e.detail.value
const startDate = this.data.chart2StartDate
const endDate = this.data.chart2EndDate
// 验证日期范围
if (field === 'startDate' && endDate && value > endDate) {
wx.showToast({
title: '开始时间不能大于结束时间',
icon: 'none',
})
return
}
if (field === 'endDate' && startDate && value < startDate) {
wx.showToast({
title: '结束时间不能小于开始时间',
icon: 'none',
})
return
}
this.setData({
[field === 'startDate' ? 'chart2StartDate' : 'chart2EndDate']: value,
})
this.getPharmacistChart() this.getPharmacistChart()
},
// 图表3日期选择变化
onChart3DateChange(e: WechatMiniprogram.CustomEvent) {
const { field } = e.currentTarget.dataset
const value = e.detail.value
const startDate = this.data.chart3StartDate
const endDate = this.data.chart3EndDate
// 验证日期范围
if (field === 'startDate' && endDate && value > endDate) {
wx.showToast({
title: '开始时间不能大于结束时间',
icon: 'none',
})
return
}
if (field === 'endDate' && startDate && value < startDate) {
wx.showToast({
title: '结束时间不能小于开始时间',
icon: 'none',
})
return
}
this.setData({
[field === 'startDate' ? 'chart3StartDate' : 'chart3EndDate']: value,
})
this.getPharmacyChart() this.getPharmacyChart()
}, },
// 切换到上一天 // 切换到上一天(只更新邀约患者统计卡片)
prevDate() { prevDate() {
const currentDate = new Date(this.data.startDate) const currentDate = new Date(this.data.startDate)
const newDate = new Date(currentDate.getTime() - 24 * 60 * 60 * 1000) const newDate = new Date(currentDate.getTime() - 24 * 60 * 60 * 1000)
const startDate = this.formatDate(newDate) const startDate = this.formatDate(newDate)
this.setData({ startDate }) this.setData({ startDate })
// 重新加载图表数据 // 只重新加载统计数据
this.getPatientChart() this.getPatientStatistics()
this.getPharmacistChart()
this.getPharmacyChart()
}, },
// 切换到下一天 // 切换到下一天(只更新邀约患者统计卡片)
nextDate() { nextDate() {
const currentDate = new Date(this.data.startDate) const currentDate = new Date(this.data.startDate)
const newDate = new Date(currentDate.getTime() + 24 * 60 * 60 * 1000) const newDate = new Date(currentDate.getTime() + 24 * 60 * 60 * 1000)
@ -291,10 +427,8 @@ Page({
const startDate = this.formatDate(newDate) const startDate = this.formatDate(newDate)
this.setData({ startDate }) this.setData({ startDate })
// 重新加载图表数据 // 只重新加载统计数据
this.getPatientChart() this.getPatientStatistics()
this.getPharmacistChart()
this.getPharmacyChart()
}, },
initChartBar(list: any[]) { initChartBar(list: any[]) {

42
src/ground/pages/home/index.wxml

@ -24,7 +24,7 @@
<view class="s-header"> <view class="s-header">
<view class="title" bind:tap="handleFold" data-key="fold1"> <view class="title" bind:tap="handleFold" data-key="fold1">
累计邀约 累计邀约
<view class="fold {{fold1&&'active'}}"> <view class="fold {{fold1&&'active'}}" wx:if="{{totalIndicationStats.length > 0}}">
{{fold1?'展开':'收起'}} {{fold1?'展开':'收起'}}
<van-icon class="icon" name="arrow-down" /> <van-icon class="icon" name="arrow-down" />
</view> </view>
@ -34,33 +34,33 @@
<view class="row1"> <view class="row1">
<view class="col"> <view class="col">
<view class="name">药店数</view> <view class="name">药店数</view>
<view class="num">{{invitePharmacyCount}}</view> <view class="num">{{totalInvitePharmacyCount}}</view>
</view> </view>
<view class="line"></view> <view class="line"></view>
<view class="col"> <view class="col">
<view class="name">药师数</view> <view class="name">药师数</view>
<view class="num">{{invitePharmacistCount}}</view> <view class="num">{{totalInvitePharmacistCount}}</view>
</view> </view>
</view> </view>
<view class="card"> <view class="card">
<view class="row2"> <view class="row2">
<view class="col"> <view class="col">
<view class="name">邀约患者数</view> <view class="name">邀约患者数</view>
<view class="num">{{invitePatientCount}}</view> <view class="num">{{totalInvitePatientCount}}</view>
</view> </view>
<view class="line"></view> <view class="line"></view>
<view class="col"> <view class="col">
<view class="name">跳转患者数</view> <view class="name">跳转患者数</view>
<view class="num">{{jumpPatientCount}}</view> <view class="num">{{totalJumpPatientCount}}</view>
</view> </view>
<view class="line"></view> <view class="line"></view>
<view class="col"> <view class="col">
<view class="name">入组患者数</view> <view class="name">入组患者数</view>
<view class="num">{{enrollPatientCount}}</view> <view class="num">{{totalEnrollPatientCount}}</view>
</view> </view>
</view> </view>
<view class="card-container {{fold1&&'fold'}}"> <view class="card-container {{fold1&&'fold'}}">
<view wx:for="{{indicationStats}}" wx:key="indicationId" class="row3"> <view wx:for="{{totalIndicationStats}}" wx:key="indicationId" class="row3">
<view class="col"> <view class="col">
<view class="name">{{item.indicationName}}</view> <view class="name">{{item.indicationName}}</view>
<view class="num">{{item.invitePatientCount}}</view> <view class="num">{{item.invitePatientCount}}</view>
@ -90,12 +90,12 @@
<view class="c-options"> <view class="c-options">
<view class="name" bind:tap="handleFold" data-key="fold2"> <view class="name" bind:tap="handleFold" data-key="fold2">
邀约患者统计 邀约患者统计
<view class="fold {{fold2&&'active'}}"> <view class="fold {{fold2&&'active'}}" wx:if="{{indicationStats.length > 0}}">
{{fold2?'展开':'收起'}} {{fold2?'展开':'收起'}}
<van-icon class="icon" name="arrow-down" /> <van-icon class="icon" name="arrow-down" />
</view> </view>
</view> </view>
<picker class="picker" mode="date" value="{{startDate}}" end="{{today}}" bindchange="onDateChange" data-field="startDate"> <picker class="picker" mode="date" value="{{startDate}}" end="{{today}}" bindchange="onDateChange">
<view class="p-content"> <view class="p-content">
<van-icon class="icon" name="arrow-left" catchtap="prevDate" /> <van-icon class="icon" name="arrow-left" catchtap="prevDate" />
<view class="content">{{startDate}}</view> <view class="content">{{startDate}}</view>
@ -140,16 +140,16 @@
</view> </view>
</view> </view>
<view class="chart-range"> <view class="chart-range">
<picker class="picker" mode="date" value="{{startDate}}" end="{{endDate}}" bindchange="onDateChange" data-field="startDate"> <picker class="picker" mode="date" value="{{chart1StartDate}}" end="{{chart1EndDate}}" bindchange="onChart1DateChange" data-field="startDate">
<view class="p-content"> <view class="p-content">
<view class="content">{{startDate}}</view> <view class="content">{{chart1StartDate}}</view>
<image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image>
</view> </view>
</picker> </picker>
<view class="line"></view> <view class="line"></view>
<picker class="picker" mode="date" value="{{endDate}}" start="{{startDate}}" bindchange="onDateChange" data-field="endDate"> <picker class="picker" mode="date" value="{{chart1EndDate}}" start="{{chart1StartDate}}" bindchange="onChart1DateChange" data-field="endDate">
<view class="p-content"> <view class="p-content">
<view class="content">{{endDate}}</view> <view class="content">{{chart1EndDate}}</view>
<image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image>
</view> </view>
</picker> </picker>
@ -168,16 +168,16 @@
<view class="title">邀约药师数统计</view> <view class="title">邀约药师数统计</view>
</view> </view>
<view class="chart-range"> <view class="chart-range">
<picker class="picker" mode="date" value="{{startDate}}" end="{{endDate}}" bindchange="onDateChange" data-field="startDate"> <picker class="picker" mode="date" value="{{chart2StartDate}}" end="{{chart2EndDate}}" bindchange="onChart2DateChange" data-field="startDate">
<view class="p-content"> <view class="p-content">
<view class="content">{{startDate}}</view> <view class="content">{{chart2StartDate}}</view>
<image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image>
</view> </view>
</picker> </picker>
<view class="line"></view> <view class="line"></view>
<picker class="picker" mode="date" value="{{endDate}}" start="{{startDate}}" bindchange="onDateChange" data-field="endDate"> <picker class="picker" mode="date" value="{{chart2EndDate}}" start="{{chart2StartDate}}" bindchange="onChart2DateChange" data-field="endDate">
<view class="p-content"> <view class="p-content">
<view class="content">{{endDate}}</view> <view class="content">{{chart2EndDate}}</view>
<image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image>
</view> </view>
</picker> </picker>
@ -192,16 +192,16 @@
<view class="title">邀约药店统计</view> <view class="title">邀约药店统计</view>
</view> </view>
<view class="chart-range"> <view class="chart-range">
<picker class="picker" mode="date" value="{{startDate}}" end="{{endDate}}" bindchange="onDateChange" data-field="startDate"> <picker class="picker" mode="date" value="{{chart3StartDate}}" end="{{chart3EndDate}}" bindchange="onChart3DateChange" data-field="startDate">
<view class="p-content"> <view class="p-content">
<view class="content">{{startDate}}</view> <view class="content">{{chart3StartDate}}</view>
<image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image>
</view> </view>
</picker> </picker>
<view class="line"></view> <view class="line"></view>
<picker class="picker" mode="date" value="{{endDate}}" start="{{startDate}}" bindchange="onDateChange" data-field="endDate"> <picker class="picker" mode="date" value="{{chart3EndDate}}" start="{{chart3StartDate}}" bindchange="onChart3DateChange" data-field="endDate">
<view class="p-content"> <view class="p-content">
<view class="content">{{endDate}}</view> <view class="content">{{chart3EndDate}}</view>
<image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image>
</view> </view>
</picker> </picker>

1
src/ground/pages/invite/index.wxml

@ -40,6 +40,5 @@
扫码立即加入 扫码立即加入
<view class="dot"></view> <view class="dot"></view>
</view> </view>
<view class="save-tip" wx:if="{{qrCodeUrl}}">点击二维码保存到相册</view>
</view> </view>
</view> </view>

72
src/ground/pages/pharmacist/index.ts

@ -8,6 +8,12 @@ Page({
startDate: '', startDate: '',
endDate: '', endDate: '',
// 项目列表
projectList: [] as Array<{ projectId: number; projectName: string; projectDescription: string }>,
currentProjectId: 0,
currentProjectName: '',
projectIndex: 0,
// 药师列表 // 药师列表
pharmacistList: [] as any[], pharmacistList: [] as any[],
totalCount: 0, totalCount: 0,
@ -26,8 +32,74 @@ Page({
onLoad() { onLoad() {
// 地推端药师页面,仅允许地推人员访问 // 地推端药师页面,仅允许地推人员访问
app.waitLogin({ types: [3] }).then(() => { app.waitLogin({ types: [3] }).then(() => {
this.getProjectList()
})
},
// 获取项目列表
getProjectList() {
wx.ajax({
method: 'GET',
url: '/app/promoter/promoter/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]
const projectIndex = projectList.findIndex((item: any) => item.projectId === currentProjectId)
this.setData({
projectList,
currentProjectId,
currentProjectName: currentProject?.projectName || '特诺雅',
projectIndex: projectIndex >= 0 ? projectIndex : 0,
})
// 获取药师列表
this.getPharmacistList()
})
},
// 切换项目
onProjectChange(e: WechatMiniprogram.CustomEvent) {
const index = e.detail.value
const project = this.data.projectList[index]
if (project && project.projectId !== this.data.currentProjectId) {
// 先调用切换项目接口
wx.ajax({
method: 'POST',
url: '/app/promoter/promoter/switch-project',
data: {
projectId: project.projectId,
},
}).then(() => {
// 切换成功后更新页面数据
this.setData({
currentProjectId: project.projectId,
currentProjectName: project.projectName,
projectIndex: index,
// 重置列表数据
page: 1,
pharmacistList: [],
hasMore: true,
pagination: {
count: 0,
page: 0,
pages: 0,
},
})
// 重新加载数据
this.getPharmacistList() this.getPharmacistList()
wx.showToast({
title: '切换成功',
icon: 'success',
}) })
}).catch(() => {
wx.showToast({
title: '切换失败',
icon: 'none',
})
})
}
}, },
// 获取药师列表 // 获取药师列表
getPharmacistList() { getPharmacistList() {

14
src/ground/pages/pharmacist/index.wxml

@ -1,9 +1,9 @@
<navbar fixed custom-style="background: #fff;"> <navbar fixed custom-style="background: #fff;">
<view class="page-switch" slot="left"> <picker class="page-switch" slot="left" mode="selector" range="{{projectList}}" range-key="projectName" value="{{projectIndex}}" bindchange="onProjectChange">
特诺雅 {{currentProjectName}}
<text style="font-size: 0.5em; vertical-align: super">®</text> <text style="font-size: 0.5em; vertical-align: super">®</text>
<van-icon class="arrow" name="arrow-down" /> <van-icon class="arrow" name="arrow-down" />
</view> </picker>
</navbar> </navbar>
<view class="page" style="padding-top:{{pageTop}}px"> <view class="page" style="padding-top:{{pageTop}}px">
@ -22,11 +22,11 @@
<view class="range"> <view class="range">
<view class="label">时间筛选:</view> <view class="label">时间筛选:</view>
<view class="r-wrap"> <view class="r-wrap">
<picker class="picker" mode="date" bindchange="handleStartDateChange" end="{{endDate || ''}}"> <picker class="picker" mode="date" value="{{startDate}}" bindchange="handleStartDateChange" end="{{endDate || ''}}">
<view class="date">{{startDate || '开始时间'}}</view> <view class="date">{{startDate || '开始时间'}}</view>
</picker> </picker>
<view class="line"></view> <view class="line"></view>
<picker class="picker" mode="date" bindchange="handleEndDateChange" start="{{startDate || ''}}"> <picker class="picker" mode="date" value="{{endDate}}" bindchange="handleEndDateChange" start="{{startDate || ''}}">
<view class="date">{{endDate || '结束时间'}}</view> <view class="date">{{endDate || '结束时间'}}</view>
</picker> </picker>
<image class="icon" src="{{imageUrl}}icon2.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon2.png?t={{Timestamp}}"></image>
@ -43,7 +43,7 @@
<view class="pharmacist-card" wx:for="{{pharmacistList}}" wx:key="id" data-id="{{item.id}}" bind:tap="handleInfo"> <view class="pharmacist-card" wx:for="{{pharmacistList}}" wx:key="id" data-id="{{item.id}}" bind:tap="handleInfo">
<!-- 药师基本信息 --> <!-- 药师基本信息 -->
<view class="user"> <view class="user">
<image class="avatar" src="{{imageUrl}}cache/a2.png?t={{Timestamp}}"></image> <image class="avatar" src="{{item.avatar}}"></image>
<view class="wrap"> <view class="wrap">
<view class="pharmacist-info"> <view class="pharmacist-info">
<text class="pharmacist-name">{{item.name}}</text> <text class="pharmacist-name">{{item.name}}</text>
@ -84,7 +84,7 @@
<view class="detail-btn">数据详情</view> <view class="detail-btn">数据详情</view>
</view> </view>
</view> </view>
<pagination pagination="{{pagination}}" wx:if="{{pharmacistList.length > 0}}" /> <pagination pagination="{{pagination}}" />
</view> </view>
</view> </view>

132
src/ground/pages/stat/index.ts

@ -2,53 +2,36 @@ const app = getApp<IAppOption>()
Page({ Page({
data: { data: {
// 药师ID
pharmacistId: '',
// 时间筛选 // 时间筛选
startDate: '', startDate: '',
endDate: '', endDate: '',
type: 'day', // day-按日,month-按月 type: 'day', // day-按日,month-按月
// 统计数据 // 统计数据(从 summary 获取)
invitePatientCount: 0, invitePatientCount: 0,
jumpPatientCount: 0, jumpPatientCount: 0,
enrollPatientCount: 0, enrollPatientCount: 0,
indicationStats: [] as any[],
// 图表数据
chartList: [] as any[],
// 加载状态 // 列表数据
loading: false, list: [] as any[],
// 分页信息 // 分页信息
page: 1,
pageSize: 20,
pagination: { pagination: {
count: 0, count: 0,
page: 1, page: 0,
pages: 1, pages: 0,
}, },
}, },
onLoad(option: { id?: string }) { onLoad() {
// 地推端统计页面,仅允许地推人员访问 // 地推端统计页面,仅允许地推人员访问
app.waitLogin({ types: [3] }).then(() => { app.waitLogin({ types: [3] }).then(() => {
// 获取药师ID // 设置默认时间范围(2026年3月至今)
const pharmacistId = option.id || ''
if (!pharmacistId) {
wx.showToast({
title: '缺少药师ID',
icon: 'none',
})
return
}
this.setData({ pharmacistId })
// 设置默认时间范围(最近30天)
this.setDefaultDateRange() this.setDefaultDateRange()
// 获取统计数据 // 获取列表数据(包含顶部统计数据)
this.getStatistics() this.getList(true)
this.getChartData()
}) })
}, },
// 设置默认时间范围(2026年3月至今) // 设置默认时间范围(2026年3月至今)
@ -68,57 +51,78 @@ Page({
const day = String(date.getDate()).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}` return `${year}-${month}-${day}`
}, },
// 获取统计数据 // 获取列表数据(包含顶部统计数据)
getStatistics() { getList(reset = false) {
const { pharmacistId } = this.data const { startDate, endDate, type, page, pageSize, pagination } = this.data
if (!pharmacistId) return
wx.ajax({ // 如果是重置(如筛选条件变化),先重置状态
method: 'GET', if (reset) {
url: '/app/promoter/promoter/pharmacist-statistics', if (pagination.page > 0 && pagination.page < pagination.pages) return // 如果正在加载,不重复请求
data: {
pharmacistId,
},
}).then((res: any) => {
this.setData({ this.setData({
invitePatientCount: res.invitePatientCount || 0, page: 1,
jumpPatientCount: res.jumpPatientCount || 0, list: [],
enrollPatientCount: res.enrollPatientCount || 0, pagination: { count: 0, page: 0, pages: 0 },
indicationStats: res.indicationStats || [],
})
}) })
}, } else {
// 获取图表数据 // 非重置情况下,如果正在加载或已加载完,不再请求
getChartData() { if (pagination.page > 0 && (pagination.page >= pagination.pages)) return
const { pharmacistId, startDate, endDate, type } = this.data }
if (!pharmacistId || !startDate || !endDate) return
this.setData({ loading: true }) const currentPage = reset ? 1 : page
wx.ajax({ wx.ajax({
method: 'GET', method: 'GET',
url: '/app/promoter/promoter/pharmacist-chart', url: '/app/promoter/promoter/patient-statistics-list',
data: { data: {
pharmacistId,
startDate, startDate,
endDate, endDate,
type, type,
page: currentPage,
pageSize,
}, },
}).then((data: any) => { }).then((res: any) => {
const chartList = data || [] const newList = res.list || []
const total = res.total || 0
const summary = res.summary || {}
// 转换数据格式
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({ this.setData({
chartList, // 顶部统计数据从 summary 获取
loading: false, invitePatientCount: summary.invitePatientCount || 0,
jumpPatientCount: summary.jumpPatientCount || 0,
enrollPatientCount: summary.enrollPatientCount || 0,
// 列表数据
list,
page: currentPage + 1,
pagination: { pagination: {
count: chartList.length, count: total,
page: 1, page: currentPage,
pages: 1, pages,
}, },
}) })
}).catch(() => {
this.setData({ loading: false })
}) })
}, },
// 加载更多
onReachBottom() {
this.getList()
},
// 开始时间选择 // 开始时间选择
handleStartDateChange(e: WechatMiniprogram.CustomEvent) { handleStartDateChange(e: WechatMiniprogram.CustomEvent) {
const startDate = e.detail.value const startDate = e.detail.value
@ -134,7 +138,7 @@ Page({
} }
this.setData({ startDate }) this.setData({ startDate })
this.getChartData() this.getList(true)
}, },
// 结束时间选择 // 结束时间选择
handleEndDateChange(e: WechatMiniprogram.CustomEvent) { handleEndDateChange(e: WechatMiniprogram.CustomEvent) {
@ -151,13 +155,13 @@ Page({
} }
this.setData({ endDate }) this.setData({ endDate })
this.getChartData() this.getList(true)
}, },
// 切换统计类型 // 切换统计类型
handleTypeChange(e: WechatMiniprogram.CustomEvent) { handleTypeChange(e: WechatMiniprogram.CustomEvent) {
const type = e.currentTarget.dataset.type const type = e.currentTarget.dataset.type
this.setData({ type }) this.setData({ type })
this.getChartData() this.getList(true)
}, },
handleBack() { handleBack() {
wx.navigateBack() wx.navigateBack()

9
src/ground/pages/stat/index.wxml

@ -8,14 +8,14 @@
<view class="page" style="padding-top: {{pageTop}}px;"> <view class="page" style="padding-top: {{pageTop}}px;">
<view class="page-header" style="top:{{pageTop}}px"> <view class="page-header" style="top:{{pageTop}}px">
<view class="chart-range"> <view class="chart-range">
<picker class="picker" mode="date" end="{{endDate}}" bindchange="handleStartDateChange"> <picker class="picker" mode="date" value="{{startDate}}" end="{{endDate}}" bindchange="handleStartDateChange">
<view class="p-content"> <view class="p-content">
<view class="content">{{startDate}}</view> <view class="content">{{startDate}}</view>
<image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image>
</view> </view>
</picker> </picker>
<view class="line"></view> <view class="line"></view>
<picker class="picker" mode="date" start="{{startDate}}" bindchange="handleEndDateChange"> <picker class="picker" mode="date" value="{{endDate}}" start="{{startDate}}" bindchange="handleEndDateChange">
<view class="p-content"> <view class="p-content">
<view class="content">{{endDate}}</view> <view class="content">{{endDate}}</view>
<image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image>
@ -47,8 +47,7 @@
</view> </view>
</view> </view>
<view class="stat-list"> <view class="stat-list">
<view class="loading" wx:if="{{loading}}">加载中...</view> <view class="module" wx:for="{{list}}" wx:key="date">
<view class="module" wx:for="{{chartList}}" wx:key="date">
<view class="aside"> <view class="aside">
<view class="line-top"></view> <view class="line-top"></view>
<view class="dot"></view> <view class="dot"></view>
@ -93,6 +92,6 @@
</view> </view>
</view> </view>
</view> </view>
<view class="empty" wx:if="{{!loading && chartList.length === 0}}">暂无数据</view> <pagination pagination="{{pagination}}" />
</view> </view>
</view> </view>

Loading…
Cancel
Save