添加接口:今日体检进度信息
This commit is contained in:
@@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 营收数据统计入参
|
* 营收数据统计入参
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user