Files
hms/apps/miniprogram/src/pages/pkg-profile/medication/index.tsx
iven 22e33114b1 feat(mp): 微信模板消息订阅统一封装
- 新增 requestSubscribe() 统一订阅函数,消除页面内类型断言重复
- 用药页面新增 requestSubscribeMessage 订阅(MEDICATION_REMINDER 模板)
- 告警页面改用 requestSubscribe('CRITICAL_HEALTH_ALERT')
- wechat-templates 新增 MEDICATION_REMINDER 模板 ID 环境变量
2026-05-22 12:08:49 +08:00

218 lines
6.9 KiB
TypeScript

import { useState, useCallback } from 'react';
import { View, Text, Input, Picker } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData';
import { getCachedPatientId } from '@/services/request';
import { requestSubscribe } from '@/services/wechat-templates';
import EmptyState from '../../../components/EmptyState';
import {
listReminders,
createReminder,
updateReminder,
deleteReminder,
type MedicationReminder,
} from '../../../services/medication-reminder';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
export default function MedicationReminder() {
const modeClass = useElderClass();
const [reminders, setReminders] = useState<MedicationReminder[]>([]);
const [loading, setLoading] = useState(true);
const [showForm, setShowForm] = useState(false);
const [formName, setFormName] = useState('');
const [formDosage, setFormDosage] = useState('');
const [formTime, setFormTime] = useState('08:00');
const fetchReminders = useCallback(async () => {
try {
const res = await listReminders();
setReminders(res.data ?? []);
} catch (err) {
console.warn('[medication] 加载失败:', err);
Taro.showToast({ title: '加载失败', icon: 'none' });
} finally {
setLoading(false);
}
}, []);
usePageData(
async () => {
await fetchReminders();
// 请求用药提醒推送订阅
requestSubscribe('MEDICATION_REMINDER');
},
{ throttleMs: 5000, enablePullDown: true },
);
const handleToggle = async (r: MedicationReminder) => {
try {
await updateReminder(r.id, {
is_active: !r.is_active,
version: r.version,
});
fetchReminders();
} catch (err) {
console.warn('[medication] 操作失败:', err);
Taro.showToast({ title: '操作失败', icon: 'none' });
}
};
const handleDelete = (r: MedicationReminder) => {
Taro.showModal({
title: '确认删除',
content: '确定要删除这个提醒吗?',
}).then(async (res) => {
if (res.confirm) {
try {
await deleteReminder(r.id, r.version);
Taro.showToast({ title: '已删除', icon: 'success' });
fetchReminders();
} catch (err) {
console.warn('[medication] 删除失败:', err);
Taro.showToast({ title: '删除失败', icon: 'none' });
}
}
});
};
const handleAdd = async () => {
if (!formName.trim()) {
Taro.showToast({ title: '请输入药品名称', icon: 'none' });
return;
}
const patientId = getCachedPatientId();
if (!patientId) {
Taro.showToast({ title: '请先绑定患者档案', icon: 'none' });
return;
}
try {
await createReminder({
patient_id: patientId,
medication_name: formName.trim(),
dosage: formDosage.trim() || undefined,
reminder_times: [formTime],
is_active: true,
});
setFormName('');
setFormDosage('');
setFormTime('08:00');
setShowForm(false);
Taro.showToast({ title: '添加成功', icon: 'success' });
fetchReminders();
} catch (err) {
console.warn('[medication] 添加失败:', err);
Taro.showToast({ title: '添加失败', icon: 'none' });
}
};
const nameInitial = (name: string) => {
return name ? name.charAt(0) : '药';
};
if (loading) {
return (
<PageShell className={modeClass}>
<Text className='page-title'></Text>
<View className='medication-loading'>
<Text className='medication-loading-text'>...</Text>
</View>
</PageShell>
);
}
return (
<PageShell className={modeClass}>
<Text className='page-title'></Text>
<View className='reminder-list'>
{reminders.map((r) => (
<View className={`reminder-card ${!r.is_active ? 'disabled' : ''}`} key={r.id}>
<View className='reminder-avatar'>
<Text className='reminder-avatar-text'>{nameInitial(r.medication_name)}</Text>
</View>
<View className='reminder-info'>
<Text className='reminder-name'>{r.medication_name}</Text>
<Text className='reminder-dosage'>
{r.dosage || '-'} | {r.reminder_times?.join(', ') || '-'}
</Text>
</View>
<View className='reminder-actions'>
<View
className={`toggle ${r.is_active ? 'on' : 'off'}`}
onClick={() => handleToggle(r)}
>
<View className='toggle-dot' />
</View>
<Text
className='delete-btn'
onClick={() => handleDelete(r)}
>
</Text>
</View>
</View>
))}
</View>
{reminders.length === 0 && (
<EmptyState text='暂无用药提醒' />
)}
{showForm && (
<View className='form-card'>
<Text className='form-card-title'></Text>
<View className='form-item'>
<Text className='form-label'></Text>
<Input
className='form-input'
placeholder='请输入药品名称'
placeholderClass='form-placeholder'
value={formName}
onInput={(e) => setFormName(e.detail.value)}
/>
</View>
<View className='form-item'>
<Text className='form-label'></Text>
<Input
className='form-input'
placeholder='如: 1片、10ml'
placeholderClass='form-placeholder'
value={formDosage}
onInput={(e) => setFormDosage(e.detail.value)}
/>
</View>
<View className='form-item'>
<Text className='form-label'></Text>
<Picker
mode='time'
value={formTime}
onChange={(e) => setFormTime(e.detail.value)}
>
<View className='time-picker-wrap'>
<Text className='time-value'>{formTime}</Text>
<Text className='time-modify'></Text>
</View>
</Picker>
</View>
<View className='form-actions'>
<View className='form-cancel' onClick={() => setShowForm(false)}>
<Text className='form-cancel-text'></Text>
</View>
<View className='form-confirm' onClick={handleAdd}>
<Text className='form-confirm-text'></Text>
</View>
</View>
</View>
)}
{!showForm && (
<View className='add-btn' onClick={() => setShowForm(true)}>
<Text className='add-text'></Text>
</View>
)}
</PageShell>
);
}