const echarts = require('../../../components/ec-canvas/echarts.js') Page({ data: { fold1: true, fold2: true, // 用户信息 promoterName: '', promoterAvatar: '', label: '邀约专员', // 累计统计数据(来自 getStatistics) totalInvitePharmacyCount: 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, jumpPatientCount: 0, enrollPatientCount: 0, indicationStats: [] as Array<{ indicationId: number indicationName: string invitePatientCount: number jumpPatientCount: number enrollPatientCount: number }>, // 图表数据 chartData: [] as Array<{ date: string, count: number }>, pharmacistChartData: [] as Array<{ date: string, count: number }>, pharmacyChartData: [] as Array<{ date: string, count: number }>, // 日期范围 - 邀约患者统计卡片(单日) startDate: '', today: '', // 日期范围 - 图表1(邀约患者统计) chart1StartDate: '', chart1EndDate: '', // 日期范围 - 图表2(邀约药师统计) chart2StartDate: '', chart2EndDate: '', // 日期范围 - 图表3(邀约药店统计) chart3StartDate: '', chart3EndDate: '', // 统计类型: day-日统计, month-月统计 statType: 'day', // 项目列表 projectList: [] as Array<{ projectId: number; projectName: string; projectDescription: string }>, currentProjectId: 0, currentProjectName: '特诺雅', projectIndex: 0, }, ecDataTrendComponent1_1: null as any, ecDataTrendComponent2_1: null as any, ecDataTrendComponent3_1: null as any, async onLoad() { const app = getApp() // 地推端页面,仅允许地推人员(3)访问 app.waitLogin({ types: [3] }).then(() => { this.getUserInfo() this.getProjectList() }) // 初始化日期 const today = this.formatDate(new Date()) const defaultStartDate = this.formatDate(new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)) this.setData({ today, // 邀约患者统计卡片(单日)- 默认为今天 startDate: today, // 图表1-3(日期范围)- 默认为最近30天 chart1StartDate: defaultStartDate, chart1EndDate: today, chart2StartDate: defaultStartDate, chart2EndDate: today, chart3StartDate: defaultStartDate, chart3EndDate: today, }) }, // 格式化日期 formatDate(date: Date): string { const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') return `${year}-${month}-${day}` }, // 获取个人信息 getUserInfo() { wx.ajax({ method: 'GET', url: '/app/promoter/promoter/profile', }).then((res: any) => { this.setData({ promoterName: res.name, promoterAvatar: res.avatar, }) }) }, // 获取项目列表 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.getStatistics() this.getPatientStatistics() this.getPatientChart() this.getPharmacistChart() this.getPharmacyChart() }) }, // 切换项目 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, }) // 重新加载数据 this.getStatistics() this.getPatientStatistics() this.getPatientChart() this.getPharmacistChart() this.getPharmacyChart() wx.showToast({ title: '切换成功', icon: 'success', }) }).catch(() => { wx.showToast({ title: '切换失败', icon: 'none', }) }) } }, // 获取统计数据看板(累计数据) getStatistics() { wx.ajax({ method: 'GET', url: '/app/promoter/promoter/statistics', }).then((res: any) => { this.setData({ // 累计数据 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, jumpPatientCount: res.jumpPatientCount || 0, enrollPatientCount: res.enrollPatientCount || 0, indicationStats: res.indicationStats || [], }) }) }, // 获取邀约患者统计图表(使用 chart1 的日期) getPatientChart() { wx.ajax({ method: 'GET', url: '/app/promoter/promoter/patient-chart', data: { type: this.data.statType, startDate: this.data.chart1StartDate, endDate: this.data.chart1EndDate, }, }).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({ chartData, }) this.initChartBar(chartData) }) }, // 获取邀约药师统计图表(使用 chart2 的日期) getPharmacistChart() { const data: any = { type: this.data.statType, startDate: this.data.chart2StartDate, endDate: this.data.chart2EndDate, } if (this.data.currentProjectId) { data.projectId = this.data.currentProjectId } wx.ajax({ method: 'GET', url: '/app/promoter/promoter/pharmacist-chart', data, }).then((list: any) => { this.setData({ pharmacistChartData: list || [], }) this.initChartLine(list || [], '#chart2_1', 'ecDataTrendComponent2_1') }) }, // 获取邀约药店统计图表(使用 chart3 的日期) getPharmacyChart() { const data: any = { type: this.data.statType, startDate: this.data.chart3StartDate, endDate: this.data.chart3EndDate, } if (this.data.currentProjectId) { data.projectId = this.data.currentProjectId } wx.ajax({ method: 'GET', url: '/app/promoter/promoter/pharmacy-chart', data, }).then((list: any) => { this.setData({ pharmacyChartData: list || [], }) this.initChartLine(list || [], '#chart3_1', 'ecDataTrendComponent3_1') }) }, // 切换统计类型 switchStatType(e: WechatMiniprogram.CustomEvent) { const type = e.currentTarget.dataset.type this.setData({ statType: type, }) this.getPatientStatistics() this.getPatientChart() this.getPharmacistChart() this.getPharmacyChart() }, // 日期选择变化(邀约患者统计卡片 - 单日选择) 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 value = e.detail.value const startDate = this.data.chart1StartDate const endDate = this.data.chart1EndDate // 验证日期范围 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' ? 'chart1StartDate' : 'chart1EndDate']: value, }) 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() }, // 图表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() }, // 切换到上一天(只更新邀约患者统计卡片) prevDate() { const currentDate = new Date(this.data.startDate) const newDate = new Date(currentDate.getTime() - 24 * 60 * 60 * 1000) const startDate = this.formatDate(newDate) this.setData({ startDate }) // 只重新加载统计数据 this.getPatientStatistics() }, // 切换到下一天(只更新邀约患者统计卡片) nextDate() { const currentDate = new Date(this.data.startDate) const newDate = new Date(currentDate.getTime() + 24 * 60 * 60 * 1000) const today = new Date() // 最大日期不能大于当前日期 if (newDate > today) { wx.showToast({ title: '不能选择未来日期', icon: 'none', }) return } const startDate = this.formatDate(newDate) this.setData({ startDate }) // 只重新加载统计数据 this.getPatientStatistics() }, initChartBar(list: any[]) { return new Promise((reslove) => { this.ecDataTrendComponent1_1 = this.selectComponent('#chart1_1') this.ecDataTrendComponent1_1.init((canvas, width, height, dpr) => { const chart = echarts.init(canvas, null, { width, height, devicePixelRatio: dpr, }) canvas.setChart(chart) const x: string[] = [] const y1: string[] = [] list.forEach((item) => { x.push(item.date || item.StatMonth) y1.push(item.count || item.MonthInvitePCount) }) const option = { legend: { top: 0, right: 0, itemWidth: 8, itemHeight: 8, icon: 'rect', lineStyle: { width: '0', }, textStyle: { color: '#B5B8BB', fontSize: '12', }, }, grid: { top: '10%', left: '3%', right: '4%', bottom: '0', containLabel: true, }, xAxis: [ { type: 'category', axisTick: { show: false, }, axisLabel: { fontSize: 10, color: '#B5B8BB', }, axisLine: { show: false, }, data: x, }, ], yAxis: [ { type: 'value', minInterval: 1, splitLine: { lineStyle: { type: 'dashed', }, }, axisLabel: { fontSize: 10, color: '#B5B8BB', formatter(value) { return Math.abs(value) }, }, }, ], series: [ { name: '邀约患者数', type: 'bar', stack: 'a', width: 4, color: '#FED877', barWidth: 12, data: y1, }, { name: '跳转患者数', type: 'bar', stack: 'a', color: '#4A8DFF', barWidth: 12, data: y1, }, { name: '入组患者数', type: 'bar', stack: 'a', color: '#3ADDC8', barWidth: 12, data: y1, }, ], dataZoom: { type: 'inside', startValue: x.length - 6, endValue: x.length - 1, filterMode: 'none', }, } chart.setOption(option) reslove(chart) return chart }) }) }, initChartLine(list: any[], name: string, componentName: string) { return new Promise((reslove) => { this[componentName] = this.selectComponent(name) this[componentName].init((canvas, width, height, dpr) => { const chart = echarts.init(canvas, null, { width, height, devicePixelRatio: dpr, }) canvas.setChart(chart) const x: string[] = [] const y1: string[] = [] list.forEach((item) => { x.push(item.date || item.StatMonth) y1.push(item.count || item.MonthInvitePCount) }) const option = { legend: { top: 0, right: 0, itemWidth: 8, itemHeight: 8, icon: 'rect', lineStyle: { width: '0', }, textStyle: { color: '#B5B8BB', fontSize: '12', }, data: [], }, grid: { top: '10%', left: '3%', right: '4%', bottom: '0', containLabel: true, }, xAxis: [ { type: 'category', axisTick: { show: false, }, axisLabel: { fontSize: 10, color: '#B5B8BB', }, axisLine: { show: false, }, data: x, }, ], yAxis: [ { type: 'value', minInterval: 1, splitLine: { lineStyle: { type: 'dashed', }, }, axisLabel: { fontSize: 10, color: '#B5B8BB', formatter(value) { return Math.abs(value) }, }, }, ], series: [ { name: '邀约患者数', type: 'line', stack: 'a', width: 4, color: '#FED877', barWidth: 12, data: y1, smooth: 0.5, label: { show: true, position: 'top', fontSize: 12, color: '#B5B8BB', }, }, ], dataZoom: { type: 'inside', startValue: x.length - 6, endValue: x.length - 1, filterMode: 'none', }, } chart.setOption(option) reslove(chart) return chart }) }) }, handleInvite() { wx.navigateTo({ url: '/ground/pages/invite/index', }) }, handleInfo() { wx.navigateTo({ url: '/ground/pages/stat/index', }) }, handleFold(e) { const { key } = e.currentTarget.dataset this.setData({ [key]: !this.data[key], }) }, })