- 医生端 18 个页面全部接入(首页/待办/告警/咨询/透析/随访/ 患者/处方/报告及其详情和创建页) - 商城子包 3 页面(商品详情/积分兑换/订单) - 患者端剩余页面(AI报告/文章/活动/设备同步/登录/随访详情/ 报告详情/知情同意/诊断/透析处方/透析记录/家庭成员添加) - 页面覆盖率:22/59 (37%) → 58/58 (100%) - useElderClass hook 统一接入模式,零样板代码 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
118 lines
4.4 KiB
TypeScript
118 lines
4.4 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { View, Text, ScrollView } from '@tarojs/components';
|
|
import Taro from '@tarojs/taro';
|
|
import * as pointsApi from '@/services/points';
|
|
import Loading from '@/components/Loading';
|
|
import EmptyState from '@/components/EmptyState';
|
|
import { useElderClass } from '../../hooks/useElderClass';
|
|
import './index.scss';
|
|
|
|
const STATUS_MAP: Record<string, { label: string; className: string }> = {
|
|
published: { label: '报名中', className: 'event-card__status--published' },
|
|
ongoing: { label: '进行中', className: 'event-card__status--ongoing' },
|
|
completed: { label: '已结束', className: 'event-card__status--completed' },
|
|
cancelled: { label: '已取消', className: 'event-card__status--cancelled' },
|
|
};
|
|
|
|
export default function EventsPage() {
|
|
const modeClass = useElderClass();
|
|
const [events, setEvents] = useState<pointsApi.OfflineEvent[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [registering, setRegistering] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
loadEvents();
|
|
}, []);
|
|
|
|
const loadEvents = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const res = await pointsApi.listOfflineEvents({ page: 1, page_size: 50, status: 'published' });
|
|
setEvents(res.data || []);
|
|
} catch {
|
|
Taro.showToast({ title: '加载失败', icon: 'none' });
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleRegister = async (event: pointsApi.OfflineEvent) => {
|
|
setRegistering(event.id);
|
|
try {
|
|
await pointsApi.registerEvent(event.id);
|
|
Taro.showToast({ title: '报名成功', icon: 'success' });
|
|
loadEvents();
|
|
} catch (err: any) {
|
|
const msg = err?.message || '报名失败';
|
|
Taro.showToast({ title: msg.substring(0, 20), icon: 'none' });
|
|
} finally {
|
|
setRegistering(null);
|
|
}
|
|
};
|
|
|
|
const formatDate = (d: string) => {
|
|
return new Date(d).toLocaleDateString('zh-CN', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
});
|
|
};
|
|
|
|
if (loading) return <Loading />;
|
|
|
|
return (
|
|
<ScrollView scrollY className={`events-page ${modeClass}`}>
|
|
<View className='events-header'>
|
|
<Text className='events-header__title'>线下活动</Text>
|
|
<Text className='events-header__subtitle'>参加活动赢取积分</Text>
|
|
</View>
|
|
|
|
{events.length === 0 ? (
|
|
<EmptyState text='暂无可报名的活动' />
|
|
) : (
|
|
<View className='event-list'>
|
|
{events.map((event) => {
|
|
const st = STATUS_MAP[event.status] || { label: event.status, className: '' };
|
|
const isFull = event.max_participants != null && event.current_participants >= event.max_participants;
|
|
const isRegistering = registering === event.id;
|
|
|
|
return (
|
|
<View key={event.id} className='event-card'>
|
|
<View className='event-card__header'>
|
|
<View className={`event-card__status ${st.className}`}>
|
|
<Text>{st.label}</Text>
|
|
</View>
|
|
<Text className='event-card__points'>+{event.points_reward} 积分</Text>
|
|
</View>
|
|
<Text className='event-card__title'>{event.title}</Text>
|
|
{event.description && (
|
|
<Text className='event-card__desc'>{event.description}</Text>
|
|
)}
|
|
<View className='event-card__info'>
|
|
<Text className='event-card__date'>{formatDate(event.event_date)}</Text>
|
|
{event.location && (
|
|
<Text className='event-card__location'>{event.location}</Text>
|
|
)}
|
|
</View>
|
|
<View className='event-card__footer'>
|
|
<Text className='event-card__participants'>
|
|
{event.current_participants}{event.max_participants ? `/${event.max_participants}` : ''} 人已报名
|
|
</Text>
|
|
<View
|
|
className={`event-card__btn ${isFull ? 'event-card__btn--disabled' : ''}`}
|
|
onClick={() => !isFull && !isRegistering && handleRegister(event)}
|
|
>
|
|
<Text className='event-card__btn-text'>
|
|
{isRegistering ? '报名中...' : isFull ? '已满' : '立即报名'}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
)}
|
|
</ScrollView>
|
|
);
|
|
}
|