分离左侧快速操作面板
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 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action === 'meal') {
|
||||||
return (
|
return (
|
||||||
<div className='fixed inset-0 z-40 flex items-center justify-center bg-black/30'>
|
<MealRegistrationModal
|
||||||
<div className='w-[560px] max-w-[95vw] bg-white rounded-3xl shadow-xl overflow-hidden text-sm'>
|
onClose={onClose}
|
||||||
<div className='px-4 py-3 border-b flex items-center justify-between'>
|
totalExamCount={totalExamCount}
|
||||||
<div className='font-semibold'>{titleMap[action]}</div>
|
mealCount={mealCount}
|
||||||
<button className='text-xs text-gray-500' onClick={onClose}>
|
notMealCount={notMealCount}
|
||||||
关闭
|
mealDoneIds={mealDoneIds}
|
||||||
</button>
|
onMealDone={onMealDone}
|
||||||
</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>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{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>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{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 === 'note') {
|
||||||
|
return <NoteModal noteText={noteText} onNoteChange={onNoteChange} onClose={onClose} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
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