分离左侧快速操作面板
This commit is contained in:
64
src/components/modals/MealRegistrationModal.tsx
Normal file
64
src/components/modals/MealRegistrationModal.tsx
Normal file
@@ -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
src/components/modals/NoteModal.tsx
Normal file
35
src/components/modals/NoteModal.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { ExamClient, QuickActionType } from '../../data/mockData';
|
||||
import { EXAM_CLIENTS } from '../../data/mockData';
|
||||
import { InfoCard } from '../ui';
|
||||
import type { QuickActionType } from '../../data/mockData';
|
||||
import { MealRegistrationModal } from './MealRegistrationModal';
|
||||
import { NoteModal } from './NoteModal';
|
||||
import { VipAuthModal } from './VipAuthModal';
|
||||
|
||||
interface QuickActionModalProps {
|
||||
action: QuickActionType;
|
||||
noteText: string;
|
||||
@@ -24,90 +26,32 @@ export const QuickActionModal = ({
|
||||
mealDoneIds,
|
||||
onMealDone,
|
||||
}: QuickActionModalProps) => {
|
||||
const titleMap: Record<Exclude<QuickActionType, 'none'>, string> = {
|
||||
meal: '用餐登记',
|
||||
vip: '太平 VIP 认证说明',
|
||||
note: '备注窗',
|
||||
};
|
||||
|
||||
if (action === 'none') {
|
||||
return null;
|
||||
}
|
||||
|
||||
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'>{titleMap[action]}</div>
|
||||
<button className='text-xs text-gray-500' onClick={onClose}>
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
)}
|
||||
if (action === 'meal') {
|
||||
return (
|
||||
<MealRegistrationModal
|
||||
onClose={onClose}
|
||||
totalExamCount={totalExamCount}
|
||||
mealCount={mealCount}
|
||||
notMealCount={notMealCount}
|
||||
mealDoneIds={mealDoneIds}
|
||||
onMealDone={onMealDone}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
{action === 'vip' && (
|
||||
<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>
|
||||
)}
|
||||
if (action === 'note') {
|
||||
return <NoteModal noteText={noteText} onNoteChange={onNoteChange} onClose={onClose} />;
|
||||
}
|
||||
|
||||
{action === 'note' && (
|
||||
<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>
|
||||
);
|
||||
if (action === 'vip') {
|
||||
return <VipAuthModal onClose={onClose} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
|
||||
38
src/components/modals/VipAuthModal.tsx
Normal file
38
src/components/modals/VipAuthModal.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user