添加iPad签到功能

This commit is contained in:
xianyi
2025-12-10 17:55:20 +08:00
parent 6d68d618b7
commit b5aaf19897
3 changed files with 133 additions and 22 deletions

View File

@@ -12,6 +12,8 @@ import type {
PhysicalExamProgressDetailResponse, PhysicalExamProgressDetailResponse,
InputCustomerDetail, InputCustomerDetail,
CustomerDetailResponse, CustomerDetailResponse,
InputMedicalExamCenterSignIn,
PhysicalExamSignInResponse,
} from './types'; } from './types';
/** /**
@@ -98,3 +100,15 @@ export const getCustomerDetail = (
).then(res => res.data); ).then(res => res.data);
}; };
/**
* iPad 体检中心签到
*/
export const signInMedicalExamCenter = (
data: InputMedicalExamCenterSignIn
): Promise<PhysicalExamSignInResponse> => {
return request.post<PhysicalExamSignInResponse>(
`${MEDICAL_EXAM_BASE_PATH}/sign-in`,
data
).then(res => res.data);
};

View File

@@ -251,3 +251,24 @@ export interface OutputCustomerDetail {
*/ */
export type CustomerDetailResponse = CommonActionResult<OutputCustomerDetail>; export type CustomerDetailResponse = CommonActionResult<OutputCustomerDetail>;
/**
* 体检中心签到入参
*/
export interface InputMedicalExamCenterSignIn {
/** 身份证号 */
id_no: string;
}
/**
* 体检中心签到出参
*/
export interface OutputPhysicalExamSignIn {
/** 是否签到成功0-成功 1-失败) */
is_success?: number | null;
}
/**
* 体检中心签到响应
*/
export type PhysicalExamSignInResponse = CommonActionResult<OutputPhysicalExamSignIn>;

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react';
import type { ExamClient, ExamModalTab } from '../../data/mockData'; import type { ExamClient, ExamModalTab } from '../../data/mockData';
import type { import type {
@@ -7,7 +7,7 @@ import type {
CustomerInfo, CustomerInfo,
PhysicalExamProgressItem, PhysicalExamProgressItem,
} from '../../api'; } from '../../api';
import { getCustomerDetail, getPhysicalExamProgressDetail } from '../../api'; import { getCustomerDetail, getPhysicalExamProgressDetail, signInMedicalExamCenter } from '../../api';
import { Button, Input } from '../ui'; import { Button, Input } from '../ui';
interface ExamModalProps { interface ExamModalProps {
@@ -175,28 +175,104 @@ export const ExamModal = ({ client, tab, onTabChange, onClose }: ExamModalProps)
); );
}; };
const ExamSignPanel = () => ( const ExamSignPanel = () => {
const [idNo, setIdNo] = useState('');
const [ocrLoading, setOcrLoading] = useState(false);
const [signLoading, setSignLoading] = useState(false);
const [message, setMessage] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement | null>(null);
const handlePickFile = () => {
fileInputRef.current?.click();
};
const mockOcr = async (file: File) => {
// 简单模拟 OCR提取文件名中的数字或返回示例身份证
const match = file.name.match(/\d{6,18}/);
await new Promise((r) => setTimeout(r, 600));
return match?.[0] || '440101199001010010';
};
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
setOcrLoading(true);
setMessage(null);
try {
const ocrId = await mockOcr(file);
setIdNo(ocrId);
} catch (err) {
console.error(err);
setMessage('OCR 识别失败,请重试或手动输入');
} finally {
setOcrLoading(false);
e.target.value = '';
}
};
const handleSign = async () => {
const trimmed = idNo.trim();
if (!trimmed) {
setMessage('请输入身份证号');
return;
}
setSignLoading(true);
setMessage(null);
try {
const res = await signInMedicalExamCenter({ id_no: trimmed });
const ok = res.Status === 200 && res.Data?.is_success === 0;
setMessage(ok ? '签到成功' : res.Message || '签到失败');
} catch (err) {
console.error(err);
setMessage('签到请求失败,请稍后重试');
} finally {
setSignLoading(false);
}
};
return (
<div className='grid grid-cols-2 gap-4 text-sm'> <div className='grid grid-cols-2 gap-4 text-sm'>
<div className='p-4 rounded-2xl border bg-gray-50/60 flex flex-col gap-3'> <div className='p-4 rounded-2xl border bg-gray-50/60 flex flex-col gap-3'>
<div className='font-medium'></div> <div className='font-medium'></div>
<div className='text-xs text-gray-500'></div> <div className='text-xs text-gray-500'>
<div className='flex gap-2 text-xs'>
<Button className='py-1.5 px-3'></Button>
<Button className='py-1.5 px-3'></Button>
</div> </div>
<div className='text-[11px] text-gray-400'></div> <div className='flex items-center gap-3'>
<Button className='py-1.5 px-3' onClick={handlePickFile} disabled={ocrLoading || signLoading}>
{ocrLoading ? '识别中...' : '扫描身份证'}
</Button>
<input
ref={fileInputRef}
type='file'
accept='image/*'
className='hidden'
onChange={handleFileChange}
/>
<Input
placeholder='身份证号'
value={idNo}
onChange={(e) => setIdNo(e.target.value)}
className='flex-1'
/>
<Button
className='py-1.5 px-4'
onClick={handleSign}
disabled={signLoading}
>
{signLoading ? '签到中...' : '签到'}
</Button>
</div>
{message && <div className='text-xs text-gray-600'>{message}</div>}
<div className='text-[11px] text-gray-400'></div>
</div> </div>
<div className='p-4 rounded-2xl border bg-gray-50/60 flex flex-col gap-3'> <div className='p-4 rounded-2xl border bg-gray-50/60 flex flex-col gap-3'>
<div className='font-medium'></div> <div className='font-medium'></div>
<div className='text-xs text-gray-500'></div> <div className='text-xs text-gray-500'></div>
{/* <div className='flex gap-2 text-xs text-gray-600'>
<Badge>阅读记录</Badge>
<Badge>签名图片</Badge>
</div> */}
<Button className='py-1.5 px-3'></Button> <Button className='py-1.5 px-3'></Button>
</div> </div>
</div> </div>
); );
};
interface AddonTag { interface AddonTag {
title: string; title: string;