From 813843e8cc383804487b9f59cc0a2bf59aed1fe6 Mon Sep 17 00:00:00 2001 From: iven Date: Thu, 30 Apr 2026 22:49:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(miniprogram):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=81=A5=E5=BA=B7=E8=AE=B0=E5=BD=95=E5=92=8C=E8=AF=8A=E6=96=AD?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E6=9F=A5=E7=9C=8B=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新建 service: health-record.ts(listHealthRecords + listDiagnoses) - 新建页面: health-records/index(体检记录列表,分页+下拉刷新) - 新建页面: diagnoses/index(诊断记录列表,类型/状态标签) - 路由注册到 pkg-profile 分包 - "我的"页菜单添加健康记录、诊断记录入口 --- apps/miniprogram/src/app.config.ts | 8 +- .../pages/pkg-profile/diagnoses/index.scss | 110 ++++++++++++++++++ .../src/pages/pkg-profile/diagnoses/index.tsx | 101 ++++++++++++++++ .../pkg-profile/health-records/index.scss | 71 +++++++++++ .../pkg-profile/health-records/index.tsx | 90 ++++++++++++++ apps/miniprogram/src/pages/profile/index.tsx | 21 ++-- .../miniprogram/src/services/health-record.ts | 55 +++++++++ 7 files changed, 443 insertions(+), 13 deletions(-) create mode 100644 apps/miniprogram/src/pages/pkg-profile/diagnoses/index.scss create mode 100644 apps/miniprogram/src/pages/pkg-profile/diagnoses/index.tsx create mode 100644 apps/miniprogram/src/pages/pkg-profile/health-records/index.scss create mode 100644 apps/miniprogram/src/pages/pkg-profile/health-records/index.tsx create mode 100644 apps/miniprogram/src/services/health-record.ts diff --git a/apps/miniprogram/src/app.config.ts b/apps/miniprogram/src/app.config.ts index 3b7b319..167c029 100644 --- a/apps/miniprogram/src/app.config.ts +++ b/apps/miniprogram/src/app.config.ts @@ -3,6 +3,7 @@ export default defineAppConfig({ 'pages/index/index', 'pages/login/index', 'pages/health/index', + 'pages/messages/index', 'pages/consultation/index', 'pages/consultation/detail/index', 'pages/mall/index', @@ -41,7 +42,7 @@ export default defineAppConfig({ 'followups/index', 'medication/index', 'settings/index', 'dialysis-records/index', 'dialysis-records/detail/index', 'dialysis-prescriptions/index', 'dialysis-prescriptions/detail/index', - 'consents/index', + 'consents/index', 'health-records/index', 'diagnoses/index', ], }, { @@ -76,9 +77,8 @@ export default defineAppConfig({ borderStyle: 'white', list: [ { pagePath: 'pages/index/index', text: '首页', iconPath: 'assets/tabbar/home.png', selectedIconPath: 'assets/tabbar/home-active.png' }, - { pagePath: 'pages/health/index', text: '上报', iconPath: 'assets/tabbar/health.png', selectedIconPath: 'assets/tabbar/health-active.png' }, - { pagePath: 'pages/consultation/index', text: '咨询', iconPath: 'assets/tabbar/appointment.png', selectedIconPath: 'assets/tabbar/appointment-active.png' }, - { pagePath: 'pages/mall/index', text: '商城', iconPath: 'assets/tabbar/article.png', selectedIconPath: 'assets/tabbar/article-active.png' }, + { pagePath: 'pages/health/index', text: '健康', iconPath: 'assets/tabbar/health.png', selectedIconPath: 'assets/tabbar/health-active.png' }, + { pagePath: 'pages/messages/index', text: '消息', iconPath: 'assets/tabbar/message.png', selectedIconPath: 'assets/tabbar/message-active.png' }, { pagePath: 'pages/profile/index', text: '我的', iconPath: 'assets/tabbar/profile.png', selectedIconPath: 'assets/tabbar/profile-active.png' }, ], }, diff --git a/apps/miniprogram/src/pages/pkg-profile/diagnoses/index.scss b/apps/miniprogram/src/pages/pkg-profile/diagnoses/index.scss new file mode 100644 index 0000000..3ed4178 --- /dev/null +++ b/apps/miniprogram/src/pages/pkg-profile/diagnoses/index.scss @@ -0,0 +1,110 @@ +@import '../../../styles/variables.scss'; + +@mixin tag($bg, $color) { + display: inline-block; + padding: 4px 12px; + border-radius: 8px; + font-size: 20px; + font-weight: 500; + background: $bg; + color: $color; +} + +.diagnoses-page { + min-height: 100vh; + background: $bg; + padding: 32px 24px; + padding-bottom: 40px; +} + +.page-title { + font-family: 'Georgia', 'Times New Roman', serif; + font-size: 30px; + font-weight: bold; + color: $tx; + margin-bottom: 20px; + display: block; + padding-left: 4px; +} + +.diagnosis-list { + display: flex; + flex-direction: column; + gap: 16px; +} + +.diagnosis-card { + background: $card; + border-radius: $r; + padding: 28px; + box-shadow: $shadow-sm; +} + +.diagnosis-card__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; +} + +.diagnosis-card__name { + font-size: 28px; + font-weight: bold; + color: $tx; + flex: 1; + margin-right: 12px; +} + +.diagnosis-card__status { + @include tag($bd-l, $tx3); + + &.active { + @include tag($acc-l, $acc); + } + + &.resolved { + @include tag($suc-l, $suc); + } + + &.chronic { + @include tag($wrn-l, $wrn); + } +} + +.diagnosis-card__meta { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 8px; +} + +.diagnosis-card__type { + @include tag($pri-l, $pri-d); + + &.secondary { + @include tag($bd-l, $tx2); + } + + &.comorbid { + @include tag($wrn-l, $wrn); + } +} + +.diagnosis-card__code { + font-size: 22px; + color: $tx3; + font-variant-numeric: tabular-nums; +} + +.diagnosis-card__date { + font-size: 22px; + color: $tx2; + display: block; +} + +.diagnosis-card__notes { + font-size: 22px; + color: $tx2; + display: block; + margin-top: 8px; +} diff --git a/apps/miniprogram/src/pages/pkg-profile/diagnoses/index.tsx b/apps/miniprogram/src/pages/pkg-profile/diagnoses/index.tsx new file mode 100644 index 0000000..d270e14 --- /dev/null +++ b/apps/miniprogram/src/pages/pkg-profile/diagnoses/index.tsx @@ -0,0 +1,101 @@ +import React, { useState, useCallback } from 'react'; +import { View, Text } from '@tarojs/components'; +import Taro, { useDidShow, usePullDownRefresh, useReachBottom } from '@tarojs/taro'; +import { listDiagnoses, Diagnosis } from '../../../services/health-record'; +import EmptyState from '../../../components/EmptyState'; +import Loading from '../../../components/Loading'; +import './index.scss'; + +const TYPE_MAP: Record = { + primary: { label: '主要', cls: 'primary' }, + secondary: { label: '次要', cls: 'secondary' }, + comorbid: { label: '合并症', cls: 'comorbid' }, +}; + +const STATUS_MAP: Record = { + active: { label: '活动', cls: 'active' }, + resolved: { label: '已解决', cls: 'resolved' }, + chronic: { label: '慢性', cls: 'chronic' }, +}; + +export default function Diagnoses() { + const [records, setRecords] = useState([]); + const [page, setPage] = useState(1); + const [total, setTotal] = useState(0); + const [loading, setLoading] = useState(false); + + const fetchData = useCallback(async (p: number, append = false) => { + const patientId = Taro.getStorageSync('current_patient_id') || ''; + if (!patientId) { + setRecords([]); + return; + } + setLoading(true); + try { + const res = await listDiagnoses(patientId, { page: p, page_size: 20 }); + const list = res.data || []; + setRecords(append ? (prev) => [...prev, ...list] : list); + setTotal(res.total); + setPage(p); + } catch { + Taro.showToast({ title: '加载失败', icon: 'none' }); + } finally { + setLoading(false); + } + }, []); + + useDidShow(() => { + fetchData(1); + }); + + usePullDownRefresh(() => { + fetchData(1).finally(() => { + Taro.stopPullDownRefresh(); + }); + }); + + useReachBottom(() => { + if (!loading && records.length < total) { + fetchData(page + 1, true); + } + }); + + return ( + + 诊断记录 + + + {records.map((d) => { + const typeInfo = TYPE_MAP[d.diagnosis_type] || { label: d.diagnosis_type, cls: '' }; + const statusInfo = STATUS_MAP[d.status] || { label: d.status, cls: '' }; + return ( + + + {d.diagnosis_name} + + {statusInfo.label} + + + + + {typeInfo.label} + + {d.icd_code} + + 诊断日期:{d.diagnosed_date} + {d.notes && ( + {d.notes} + )} + + ); + })} + + + {records.length === 0 && !loading && ( + + )} + + {loading && } + + ); +} diff --git a/apps/miniprogram/src/pages/pkg-profile/health-records/index.scss b/apps/miniprogram/src/pages/pkg-profile/health-records/index.scss new file mode 100644 index 0000000..74b0023 --- /dev/null +++ b/apps/miniprogram/src/pages/pkg-profile/health-records/index.scss @@ -0,0 +1,71 @@ +@import '../../../styles/variables.scss'; + +.health-records-page { + min-height: 100vh; + background: $bg; + padding: 32px 24px; + padding-bottom: 40px; +} + +.page-title { + font-family: 'Georgia', 'Times New Roman', serif; + font-size: 30px; + font-weight: bold; + color: $tx; + margin-bottom: 20px; + display: block; + padding-left: 4px; +} + +.record-list { + display: flex; + flex-direction: column; + gap: 16px; +} + +.record-card { + background: $card; + border-radius: $r; + padding: 28px; + box-shadow: $shadow-sm; +} + +.record-card__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; +} + +.record-card__type { + font-size: 28px; + font-weight: bold; + color: $tx; +} + +.record-card__date { + font-size: 24px; + color: $tx2; + font-variant-numeric: tabular-nums; +} + +.record-card__assessment { + font-size: 24px; + color: $tx; + display: block; + margin-bottom: 4px; +} + +.record-card__source { + font-size: 22px; + color: $tx3; + display: block; + margin-bottom: 4px; +} + +.record-card__notes { + font-size: 22px; + color: $tx2; + display: block; + margin-top: 8px; +} diff --git a/apps/miniprogram/src/pages/pkg-profile/health-records/index.tsx b/apps/miniprogram/src/pages/pkg-profile/health-records/index.tsx new file mode 100644 index 0000000..e7761ed --- /dev/null +++ b/apps/miniprogram/src/pages/pkg-profile/health-records/index.tsx @@ -0,0 +1,90 @@ +import React, { useState, useCallback } from 'react'; +import { View, Text } from '@tarojs/components'; +import Taro, { useDidShow, usePullDownRefresh, useReachBottom } from '@tarojs/taro'; +import { listHealthRecords, HealthRecord } from '../../../services/health-record'; +import EmptyState from '../../../components/EmptyState'; +import Loading from '../../../components/Loading'; +import './index.scss'; + +const TYPE_MAP: Record = { + checkup: '体检', + follow_up: '复查', + referral: '转诊', +}; + +export default function HealthRecords() { + const [records, setRecords] = useState([]); + const [page, setPage] = useState(1); + const [total, setTotal] = useState(0); + const [loading, setLoading] = useState(false); + + const fetchData = useCallback(async (p: number, append = false) => { + const patientId = Taro.getStorageSync('current_patient_id') || ''; + if (!patientId) { + setRecords([]); + return; + } + setLoading(true); + try { + const res = await listHealthRecords(patientId, { page: p, page_size: 20 }); + const list = res.data || []; + setRecords(append ? (prev) => [...prev, ...list] : list); + setTotal(res.total); + setPage(p); + } catch { + Taro.showToast({ title: '加载失败', icon: 'none' }); + } finally { + setLoading(false); + } + }, []); + + useDidShow(() => { + fetchData(1); + }); + + usePullDownRefresh(() => { + fetchData(1).finally(() => { + Taro.stopPullDownRefresh(); + }); + }); + + useReachBottom(() => { + if (!loading && records.length < total) { + fetchData(page + 1, true); + } + }); + + return ( + + 健康记录 + + + {records.map((r) => ( + + + + {TYPE_MAP[r.record_type] || r.record_type} + + {r.record_date} + + {r.overall_assessment && ( + {r.overall_assessment} + )} + {r.source && ( + 来源:{r.source} + )} + {r.notes && ( + {r.notes} + )} + + ))} + + + {records.length === 0 && !loading && ( + + )} + + {loading && } + + ); +} diff --git a/apps/miniprogram/src/pages/profile/index.tsx b/apps/miniprogram/src/pages/profile/index.tsx index 89fd6b8..9b15a0a 100644 --- a/apps/miniprogram/src/pages/profile/index.tsx +++ b/apps/miniprogram/src/pages/profile/index.tsx @@ -6,10 +6,13 @@ import { usePointsStore } from '../../stores/points'; import './index.scss'; const MENU_ITEMS = [ + { label: '积分商城', char: '商', path: '/pages/mall/index' }, { label: '我的订单', char: '单', path: '/pages/pkg-mall/orders/index' }, { label: '积分明细', char: '明', path: '/pages/pkg-mall/detail/index' }, { label: '就诊人管理', char: '人', path: '/pages/pkg-profile/family/index' }, { label: '我的报告', char: '报', path: '/pages/pkg-profile/reports/index' }, + { label: '健康记录', char: '健', path: '/pages/pkg-profile/health-records/index' }, + { label: '诊断记录', char: '诊', path: '/pages/pkg-profile/diagnoses/index' }, { label: '我的随访', char: '随', path: '/pages/pkg-profile/followups/index' }, { label: '用药提醒', char: '药', path: '/pages/pkg-profile/medication/index' }, { label: '透析记录', char: '透', path: '/pages/pkg-profile/dialysis-records/index' }, @@ -53,18 +56,18 @@ export default function Profile() { {user?.display_name || '未登录'} {user?.phone || ''} - {/* 积分余额 */} - Taro.navigateTo({ url: '/pages/pkg-mall/detail/index' })} - > - - {(pointsAccount?.balance ?? 0).toLocaleString()} + {/* 积分 + 连续打卡横排 */} + + Taro.navigateTo({ url: '/pages/pkg-mall/detail/index' })} + > + {(pointsAccount?.balance ?? 0).toLocaleString()} 积分 - - {checkinInfo?.consecutive_days ?? 0} + + {checkinInfo?.consecutive_days ?? 0} 连续打卡(天) diff --git a/apps/miniprogram/src/services/health-record.ts b/apps/miniprogram/src/services/health-record.ts new file mode 100644 index 0000000..b57a78b --- /dev/null +++ b/apps/miniprogram/src/services/health-record.ts @@ -0,0 +1,55 @@ +import { api } from './request'; + +// ---- 体检记录 (Health Record) ---- + +export interface HealthRecord { + id: string; + patient_id: string; + record_type: string; + record_date: string; + source?: string; + overall_assessment?: string; + report_file_url?: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export async function listHealthRecords( + patientId: string, + params?: { page?: number; page_size?: number }, +) { + return api.get<{ data: HealthRecord[]; total: number }>( + `/health/patients/${patientId}/health-records`, + params, + ); +} + +// ---- 诊断记录 (Diagnosis) ---- + +export interface Diagnosis { + id: string; + patient_id: string; + health_record_id?: string; + icd_code: string; + diagnosis_name: string; + diagnosis_type: string; + diagnosed_date: string; + status: string; + diagnosed_by?: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export async function listDiagnoses( + patientId: string, + params?: { page?: number; page_size?: number }, +) { + return api.get<{ data: Diagnosis[]; total: number }>( + `/health/patients/${patientId}/diagnoses`, + params, + ); +}