分离左侧快速操作面板

This commit is contained in:
xianyi
2025-12-16 10:12:28 +08:00
parent 5b8e74c072
commit bf864de805
4 changed files with 162 additions and 81 deletions
@@ -0,0 +1,64 @@
import type { ExamClient } from '../../data/mockData';
import { EXAM_CLIENTS } from '../../data/mockData';
import { InfoCard } from '../ui';
interface MealRegistrationModalProps {
onClose: () => void;
totalExamCount: number;
mealCount: number;
notMealCount: number;
mealDoneIds: string[];
onMealDone: (id: string) => void;
}
export const MealRegistrationModal = ({
onClose,
totalExamCount,
mealCount,
notMealCount,
mealDoneIds,
onMealDone,
}: MealRegistrationModalProps) => {
return (
<div className='fixed inset-0 z-40 flex items-center justify-center bg-black/30'>
<div className='w-[560px] max-w-[95vw] bg-white rounded-3xl shadow-xl overflow-hidden text-sm'>
<div className='px-4 py-3 border-b flex items-center justify-between'>
<div className='font-semibold'>用餐登记</div>
<button className='text-xs text-gray-500' onClick={onClose}>
关闭
</button>
</div>
<div className='px-4 py-4 bg-gray-50/60'>
<div className='space-y-3'>
<div className='grid grid-cols-3 gap-3 text-xs'>
<InfoCard label='今日体检人数' value={totalExamCount} />
<InfoCard label='已用餐人数' value={mealCount} />
<InfoCard label='未用餐人数' value={notMealCount} />
</div>
<div className='text-xs text-gray-600 mt-2 mb-1'>选择已用餐客户进行登记:</div>
<div className='max-h-60 overflow-auto border rounded-2xl bg-white p-2 text-xs'>
{EXAM_CLIENTS.map((c: ExamClient) => {
const checked = mealDoneIds.includes(c.id);
return (
<label
key={c.id}
className='flex items-center justify-between px-3 py-1.5 rounded-2xl hover:bg-gray-50 cursor-pointer'
>
<span>
{c.name} <span className='text-gray-400 text-[11px]'>({c.id})</span>
</span>
<span className='flex items-center gap-2'>
<span className='text-gray-400'>{c.status}</span>
<input type='checkbox' checked={checked} onChange={() => onMealDone(c.id)} />
</span>
</label>
);
})}
</div>
</div>
</div>
</div>
</div>
);
};
+35
View File
@@ -0,0 +1,35 @@
interface NoteModalProps {
noteText: string;
onNoteChange: (v: string) => void;
onClose: () => void;
}
export const NoteModal = ({ noteText, onNoteChange, onClose }: NoteModalProps) => {
return (
<div className='fixed inset-0 z-40 flex items-center justify-center bg-black/30'>
<div className='w-[560px] max-w-[95vw] bg-white rounded-3xl shadow-xl overflow-hidden text-sm'>
<div className='px-4 py-3 border-b flex items-center justify-between'>
<div className='font-semibold'>备注窗</div>
<button className='text-xs text-gray-500' onClick={onClose}>
关闭
</button>
</div>
<div className='px-4 py-4 bg-gray-50/60'>
<div className='space-y-3 text-xs text-gray-700'>
<div>体检客户服务备注(仅内部可见)</div>
<textarea
className='w-full rounded-2xl border px-3 py-2 text-xs outline-none focus:ring-2 focus:ring-gray-200 min-h-[96px]'
placeholder='例如:客户有既往疾病史、沟通偏好、特殊关怀需求等,可在此记录。'
value={noteText}
onChange={(e) => onNoteChange(e.target.value)}
/>
<div className='text-right text-[11px] text-gray-500'>
备注内容会同步至客户详情页,供前台和导检护士查看。
</div>
</div>
</div>
</div>
</div>
);
};
+25 -81
View File
@@ -1,6 +1,8 @@
import type { ExamClient, QuickActionType } from '../../data/mockData'; import type { QuickActionType } from '../../data/mockData';
import { EXAM_CLIENTS } from '../../data/mockData'; import { MealRegistrationModal } from './MealRegistrationModal';
import { InfoCard } from '../ui'; import { NoteModal } from './NoteModal';
import { VipAuthModal } from './VipAuthModal';
interface QuickActionModalProps { interface QuickActionModalProps {
action: QuickActionType; action: QuickActionType;
noteText: string; noteText: string;
@@ -24,90 +26,32 @@ export const QuickActionModal = ({
mealDoneIds, mealDoneIds,
onMealDone, onMealDone,
}: QuickActionModalProps) => { }: QuickActionModalProps) => {
const titleMap: Record<Exclude<QuickActionType, 'none'>, string> = {
meal: '用餐登记',
vip: '太平 VIP 认证说明',
note: '备注窗',
};
if (action === 'none') { if (action === 'none') {
return null; return null;
} }
return ( if (action === 'meal') {
<div className='fixed inset-0 z-40 flex items-center justify-center bg-black/30'> return (
<div className='w-[560px] max-w-[95vw] bg-white rounded-3xl shadow-xl overflow-hidden text-sm'> <MealRegistrationModal
<div className='px-4 py-3 border-b flex items-center justify-between'> onClose={onClose}
<div className='font-semibold'>{titleMap[action]}</div> totalExamCount={totalExamCount}
<button className='text-xs text-gray-500' onClick={onClose}> mealCount={mealCount}
关闭 notMealCount={notMealCount}
</button> mealDoneIds={mealDoneIds}
</div> onMealDone={onMealDone}
<div className='px-4 py-4 bg-gray-50/60'> />
{action === 'meal' && ( );
<div className='space-y-3'> }
<div className='grid grid-cols-3 gap-3 text-xs'>
<InfoCard label='今日体检人数' value={totalExamCount} />
<InfoCard label='已用餐人数' value={mealCount} />
<InfoCard label='未用餐人数' value={notMealCount} />
</div>
<div className='text-xs text-gray-600 mt-2 mb-1'>选择已用餐客户进行登记:</div>
<div className='max-h-60 overflow-auto border rounded-2xl bg-white p-2 text-xs'>
{EXAM_CLIENTS.map((c: ExamClient) => {
const checked = mealDoneIds.includes(c.id);
return (
<label
key={c.id}
className='flex items-center justify-between px-3 py-1.5 rounded-2xl hover:bg-gray-50 cursor-pointer'
>
<span>
{c.name} <span className='text-gray-400 text-[11px]'>({c.id})</span>
</span>
<span className='flex items-center gap-2'>
<span className='text-gray-400'>{c.status}</span>
<input type='checkbox' checked={checked} onChange={() => onMealDone(c.id)} />
</span>
</label>
);
})}
</div>
</div>
)}
{action === 'vip' && ( if (action === 'note') {
<div className='flex gap-4 items-center'> return <NoteModal noteText={noteText} onNoteChange={onNoteChange} onClose={onClose} />;
<div className='flex-1 text-xs text-gray-700 space-y-2'> }
<p>通过「太平 VIP 认证」二维码,可完成太平渠道客户的身份绑定与权益确认。</p>
<ul className='list-disc ml-5 space-y-1'>
<li>客户出示太平 APP 内会员二维码,由工作人员扫码完成认证。</li>
<li>认证成功后,系统会自动标记为「太平 VIP 客户」,并记录在体检档案中。</li>
<li>支持后续报告寄送、复查预约等专属服务。</li>
</ul>
</div>
<div className='w-40 h-40 rounded-3xl bg-white border flex items-center justify-center text-xs text-gray-500'>
<img src="https://datacenter-open.oss-cn-hangzhou.aliyuncs.com/his/taiping-vip.jpg" alt='太平 VIP 认证二维码' className='w-full h-full object-cover' />
</div>
</div>
)}
{action === 'note' && ( if (action === 'vip') {
<div className='space-y-3 text-xs text-gray-700'> return <VipAuthModal onClose={onClose} />;
<div>体检客户服务备注(仅内部可见)</div> }
<textarea
className='w-full rounded-2xl border px-3 py-2 text-xs outline-none focus:ring-2 focus:ring-gray-200 min-h-[96px]' return null;
placeholder='例如:客户有既往疾病史、沟通偏好、特殊关怀需求等,可在此记录。'
value={noteText}
onChange={(e) => onNoteChange(e.target.value)}
/>
<div className='text-right text-[11px] text-gray-500'>
备注内容会同步至客户详情页,供前台和导检护士查看。
</div>
</div>
)}
</div>
</div>
</div>
);
}; };
+38
View File
@@ -0,0 +1,38 @@
interface VipAuthModalProps {
onClose: () => void;
}
export const VipAuthModal = ({ onClose }: VipAuthModalProps) => {
return (
<div className='fixed inset-0 z-40 flex items-center justify-center bg-black/30'>
<div className='w-[560px] max-w-[95vw] bg-white rounded-3xl shadow-xl overflow-hidden text-sm'>
<div className='px-4 py-3 border-b flex items-center justify-between'>
<div className='font-semibold'>太平 VIP 认证说明</div>
<button className='text-xs text-gray-500' onClick={onClose}>
关闭
</button>
</div>
<div className='px-4 py-4 bg-gray-50/60'>
<div className='flex gap-4 items-center'>
<div className='flex-1 text-xs text-gray-700 space-y-2'>
<p>通过「太平 VIP 认证」二维码,可完成太平渠道客户的身份绑定与权益确认。</p>
<ul className='list-disc ml-5 space-y-1'>
<li>客户出示太平 APP 内会员二维码,由工作人员扫码完成认证。</li>
<li>认证成功后,系统会自动标记为「太平 VIP 客户」,并记录在体检档案中。</li>
<li>支持后续报告寄送、复查预约等专属服务。</li>
</ul>
</div>
<div className='w-40 h-40 rounded-3xl bg-white border flex items-center justify-center text-xs text-gray-500'>
<img
src='https://datacenter-open.oss-cn-hangzhou.aliyuncs.com/his/taiping-vip.jpg'
alt='太平 VIP 认证二维码'
className='w-full h-full object-cover'
/>
</div>
</div>
</div>
</div>
</div>
);
};