添加接口:今日体检进度信息

This commit is contained in:
xianyi
2025-12-10 15:28:45 +08:00
parent 9c8b679689
commit f4d5c085ee
4 changed files with 186 additions and 121 deletions

View File

@@ -4,6 +4,8 @@ import type {
TodayExamStatisticsResponse, TodayExamStatisticsResponse,
InputRevenueStatisticsInfo, InputRevenueStatisticsInfo,
RevenueStatisticsResponse, RevenueStatisticsResponse,
InputTodayExamProgress,
TodayExamProgressResponse,
} from './types'; } from './types';
/** /**
@@ -46,3 +48,17 @@ export const getRevenueStatistics = (
).then(res => res.data); ).then(res => res.data);
}; };
/**
* 今日体检进度信息
* @param data 请求参数(空对象)
* @returns 今日体检进度信息
*/
export const getTodayExamProgress = (
data: InputTodayExamProgress = {}
): Promise<TodayExamProgressResponse> => {
return request.post<TodayExamProgressResponse>(
`${MEDICAL_EXAM_BASE_PATH}/today-exam-progress`,
data
).then(res => res.data);
};

View File

@@ -55,6 +55,32 @@ export interface OutputTodayExamStatisticsInfo {
*/ */
export type TodayExamStatisticsResponse = CommonActionResult<OutputTodayExamStatisticsInfo>; export type TodayExamStatisticsResponse = CommonActionResult<OutputTodayExamStatisticsInfo>;
/**
* 今日体检进度入参
*/
export interface InputTodayExamProgress {
// 空对象,无需参数
}
/**
* 今日体检进度出参
*/
export interface OutputTodayExamProgress {
/** 今日预约人数 */
today_appointment_count?: number | null;
/** 今日已签到人数 */
today_signin_count?: number | null;
/** 今日体检中人数 */
today_in_exam_count?: number | null;
/** 今日用餐人数 */
today_meal_count?: number | null;
}
/**
* 今日体检进度接口返回
*/
export type TodayExamProgressResponse = CommonActionResult<OutputTodayExamProgress>;
/** /**
* 营收数据统计入参 * 营收数据统计入参
*/ */

View File

@@ -1,5 +1,7 @@
import { useEffect, useState } from 'react';
import type { ExamClient, ExamModalTab } from '../../data/mockData'; import type { ExamClient, ExamModalTab } from '../../data/mockData';
import { EXAM_STATS, EXAM_TAGS } from '../../data/mockData'; import { EXAM_TAGS } from '../../data/mockData';
import { getTodayExamProgress } from '../../api';
import { Badge, Card, CardContent, CardHeader, InfoCard } from '../ui'; import { Badge, Card, CardContent, CardHeader, InfoCard } from '../ui';
import { cls } from '../../utils/cls'; import { cls } from '../../utils/cls';
@@ -17,126 +19,153 @@ export const ExamSection = ({
examFilterTag, examFilterTag,
onFilterChange, onFilterChange,
onOpenModal, onOpenModal,
}: ExamSectionProps) => ( }: ExamSectionProps) => {
<div className='space-y-4'> const [progressStats, setProgressStats] = useState([
<Card> { label: '预约人数', value: 0 },
<CardHeader></CardHeader> { label: '已签到', value: 0 },
<CardContent> { label: '体检中', value: 0 },
<div className='grid grid-cols-4 gap-3'> { label: '用餐', value: 0 },
{EXAM_STATS.map(([label, value]) => ( ]);
<InfoCard key={label} label={label} value={value} />
))}
</div>
</CardContent>
</Card>
<Card> useEffect(() => {
<CardHeader> getTodayExamProgress({})
<span></span> .then((res) => {
<div className='flex items-center gap-2 text-xs'> const d = res.Data;
{EXAM_TAGS.map((tag) => ( setProgressStats([
<button { label: '预约人数', value: Number(d?.today_appointment_count ?? 0) },
key={tag} { label: '已签到', value: Number(d?.today_signin_count ?? 0) },
onClick={() => onFilterChange(tag)} { label: '体检中', value: Number(d?.today_in_exam_count ?? 0) },
className={cls( { label: '用餐', value: Number(d?.today_meal_count ?? 0) },
'px-3 py-1 rounded-2xl border', ]);
examFilterTag === tag ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700', })
)} .catch((err) => {
> console.error('获取今日体检进度失败', err);
{tag} });
</button> }, []);
))}
</div>
</CardHeader>
<CardContent>
<div className='grid grid-cols-3 gap-3 text-sm'>
{filteredClients.map((client) => {
const signDone = client.signStatus === '已登记' || client.checkedItems.includes('签到');
const addonCount = client.addonCount || 0;
const printDone = !!client.guidePrinted;
const openModal = (tab: ExamModalTab) => onOpenModal(client.id, tab);
return ( return (
<div className='space-y-4'>
<Card>
<CardHeader></CardHeader>
<CardContent>
<div className='grid grid-cols-4 gap-3'>
{progressStats.map(({ label, value }) => (
<InfoCard key={label} label={label} value={value} />
))}
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<span></span>
<div className='flex items-center gap-2 text-xs'>
{EXAM_TAGS.map((tag) => (
<button <button
key={client.id} key={tag}
onClick={() => openModal('detail')} onClick={() => onFilterChange(tag)}
className={cls( className={cls(
'text-left p-3 rounded-2xl border bg-white hover:bg-gray-50 flex flex-col gap-1', 'px-3 py-1 rounded-2xl border',
selectedExamClient.id === client.id && 'border-gray-900 bg-gray-50', examFilterTag === tag ? 'bg-gray-900 text-white border-gray-900' : 'bg-white text-gray-700',
)} )}
> >
<div className='flex items-center justify-between mb-1'> {tag}
<span className='font-medium'>{client.name}</span>
<Badge>{client.level}</Badge>
</div>
<div className='text-xs text-gray-500 truncate'>{client.packageName}</div>
<div className='flex items-center justify-between text-xs text-gray-500 mt-1'>
<span>{client.status}</span>
<span>{client.elapsed}</span>
</div>
<div className='mt-2 flex flex-wrap gap-1 text-[11px]'>
<button
type='button'
className='px-2 py-0.5 rounded-2xl border bg-white hover:bg-gray-100 flex items-center gap-1'
onClick={(e) => {
e.stopPropagation();
openModal('detail');
}}
>
<span></span>
</button>
<button
type='button'
className='px-2 py-0.5 rounded-2xl border bg-white hover:bg-gray-100 flex items-center gap-1'
onClick={(e) => {
e.stopPropagation();
openModal('sign');
}}
>
<span></span>
{signDone && <span></span>}
</button>
<button
type='button'
className='px-2 py-0.5 rounded-2xl border bg-white hover:bg-gray-100 flex items-center gap-1'
onClick={(e) => {
e.stopPropagation();
openModal('addon');
}}
>
<span></span>
{addonCount > 0 && <span className='opacity-80'>({addonCount})</span>}
</button>
<button
type='button'
className='px-2 py-0.5 rounded-2xl border bg-white hover:bg-gray-100 flex items-center gap-1'
onClick={(e) => {
e.stopPropagation();
openModal('print');
}}
>
<span></span>
{printDone && <span></span>}
</button>
<button
type='button'
className='px-2 py-0.5 rounded-2xl border bg-white hover:bg-gray-100 flex items-center gap-1'
onClick={(e) => {
e.stopPropagation();
openModal('delivery');
}}
>
<span></span>
</button>
</div>
</button> </button>
); ))}
})} </div>
</div> </CardHeader>
</CardContent> <CardContent>
</Card> <div className='grid grid-cols-3 gap-3 text-sm'>
</div> {filteredClients.map((client) => {
); const signDone = client.signStatus === '已登记' || client.checkedItems.includes('签到');
const addonCount = client.addonCount || 0;
const printDone = !!client.guidePrinted;
const openModal = (tab: ExamModalTab) => onOpenModal(client.id, tab);
return (
<div
key={client.id}
role='button'
tabIndex={0}
onClick={() => openModal('detail')}
className={cls(
'text-left p-3 rounded-2xl border bg-white hover:bg-gray-50 flex flex-col gap-1 cursor-pointer',
selectedExamClient.id === client.id && 'border-gray-900 bg-gray-50',
)}
>
<div className='flex items-center justify-between mb-1'>
<span className='font-medium'>{client.name}</span>
<Badge>{client.level}</Badge>
</div>
<div className='text-xs text-gray-500 truncate'>{client.packageName}</div>
<div className='flex items-center justify-between text-xs text-gray-500 mt-1'>
<span>{client.status}</span>
<span>{client.elapsed}</span>
</div>
<div className='mt-2 flex flex-wrap gap-1 text-[11px]'>
<button
type='button'
className='px-2 py-0.5 rounded-2xl border bg-white hover:bg-gray-100 flex items-center gap-1'
onClick={(e) => {
e.stopPropagation();
openModal('detail');
}}
>
<span></span>
</button>
<button
type='button'
className='px-2 py-0.5 rounded-2xl border bg-white hover:bg-gray-100 flex items-center gap-1'
onClick={(e) => {
e.stopPropagation();
openModal('sign');
}}
>
<span></span>
{signDone && <span></span>}
</button>
<button
type='button'
className='px-2 py-0.5 rounded-2xl border bg-white hover:bg-gray-100 flex items-center gap-1'
onClick={(e) => {
e.stopPropagation();
openModal('addon');
}}
>
<span></span>
{addonCount > 0 && <span className='opacity-80'>({addonCount})</span>}
</button>
<button
type='button'
className='px-2 py-0.5 rounded-2xl border bg-white hover:bg-gray-100 flex items-center gap-1'
onClick={(e) => {
e.stopPropagation();
openModal('print');
}}
>
<span></span>
{printDone && <span></span>}
</button>
<button
type='button'
className='px-2 py-0.5 rounded-2xl border bg-white hover:bg-gray-100 flex items-center gap-1'
onClick={(e) => {
e.stopPropagation();
openModal('delivery');
}}
>
<span></span>
</button>
</div>
</div>
);
})}
</div>
</CardContent>
</Card>
</div>
);
};

View File

@@ -226,12 +226,6 @@ export const EXAM_CLIENTS: ExamClient[] = [
}, },
]; ];
export const EXAM_STATS: [string, number][] = [
['预约人数', EXAM_CLIENTS.length],
['已签到', EXAM_CLIENTS.filter((c) => c.status === '已签到').length],
['体检中', EXAM_CLIENTS.filter((c) => c.status === '体检中').length],
['用餐', EXAM_CLIENTS.filter((c) => c.status === '用餐').length],
];
export const EXAM_TAGS = ['全部', '上午', '下午', '高客', '普客', '已登记', '未登记', '散客', '团客'] as const; export const EXAM_TAGS = ['全部', '上午', '下午', '高客', '普客', '已登记', '未登记', '散客', '团客'] as const;