1.0
This commit is contained in:
1697
package-lock.json
generated
1697
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,13 +6,10 @@ const API_CONFIG = {
|
|||||||
// 内网地址(HTTP)
|
// 内网地址(HTTP)
|
||||||
INTERNAL_URL: 'http://10.1.5.118:8077/platform-api',
|
INTERNAL_URL: 'http://10.1.5.118:8077/platform-api',
|
||||||
// 外网地址(HTTPS)
|
// 外网地址(HTTPS)
|
||||||
EXTERNAL_URL: 'https://apihis.circleharmonyhospital.cn:8982/platform-api',
|
EXTERNAL_URL: 'http://apihis.circleharmonyhospital.cn:8982/platform-api',
|
||||||
// 默认使用外网地址,可根据环境变量切换
|
|
||||||
// 开发环境使用内网,生产环境使用外网
|
|
||||||
BASE_URL: import.meta.env.NODE_ENV === 'development'
|
BASE_URL: import.meta.env.NODE_ENV === 'development'
|
||||||
? 'http://10.1.5.118:8077/platform-api'
|
? 'http://10.1.5.118:8077/platform-api'
|
||||||
: '/platform-api',
|
: 'http://apihis.circleharmonyhospital.cn:8982/platform-api',
|
||||||
// 请求超时时间(120秒)
|
|
||||||
TIMEOUT: 120000,
|
TIMEOUT: 120000,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,7 +60,10 @@ request.interceptors.response.use(
|
|||||||
localStorage.removeItem('operatorName');
|
localStorage.removeItem('operatorName');
|
||||||
localStorage.removeItem('operatorUsername');
|
localStorage.removeItem('operatorUsername');
|
||||||
// 跳转到首页并添加登录参数
|
// 跳转到首页并添加登录参数
|
||||||
window.location.href = '/home?login=true';
|
const baseUrl = import.meta.env.NODE_ENV === 'development' ? '/' : '/tijian-zongkong/';
|
||||||
|
window.location.href = `${baseUrl}#/home?login=true`;
|
||||||
|
// navigate('/home?login=true');
|
||||||
|
// return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 403:
|
case 403:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useMemo, useState, useRef, useCallback } from 'react';
|
import { useEffect, useMemo, useState, useRef, useCallback } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import type { ExamClient } from '../../data/mockData';
|
import type { ExamClient } from '../../data/mockData';
|
||||||
import { searchPhysicalExamAddItem, getAddItemCustomerInfo, getChannelCompanyList, createNativePaymentQrcode, checkNativePaymentStatus, getAddItemBillPdf, getCustomSettlementApproveStatus, customSettlementApply, customSettlementApplyCancel } from '../../api';
|
import { searchPhysicalExamAddItem, getAddItemCustomerInfo, getChannelCompanyList, createNativePaymentQrcode, checkNativePaymentStatus, getAddItemBillPdf, getCustomSettlementApproveStatus, customSettlementApply, customSettlementApplyCancel } from '../../api';
|
||||||
@@ -30,6 +31,7 @@ interface ExamAddonPanelProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
const [addonList, setAddonList] = useState<AddonItem[]>([]);
|
const [addonList, setAddonList] = useState<AddonItem[]>([]);
|
||||||
const allAddons = useMemo(() => addonList, [addonList]);
|
const allAddons = useMemo(() => addonList, [addonList]);
|
||||||
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
||||||
@@ -589,7 +591,8 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
|
|
||||||
// 等待计时器:当审核中时开始计时
|
// 等待计时器:当审核中时开始计时
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (customSettlementStatus?.apply_status === 1) {
|
const isWaiting = customSettlementStatus?.apply_status === 1 || (paymentLoading && !showQrcodeModal);
|
||||||
|
if (isWaiting) {
|
||||||
// 开始计时
|
// 开始计时
|
||||||
setWaitingSeconds(0);
|
setWaitingSeconds(0);
|
||||||
waitingTimerRef.current = window.setInterval(() => {
|
waitingTimerRef.current = window.setInterval(() => {
|
||||||
@@ -610,7 +613,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
waitingTimerRef.current = null;
|
waitingTimerRef.current = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [customSettlementStatus?.apply_status]);
|
}, [customSettlementStatus?.apply_status, paymentLoading, showQrcodeModal]);
|
||||||
|
|
||||||
// 提交自定义结算申请
|
// 提交自定义结算申请
|
||||||
const handleSubmitCustomSettlement = async () => {
|
const handleSubmitCustomSettlement = async () => {
|
||||||
@@ -678,7 +681,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
const apply_user = localStorage.getItem('operatorName');
|
const apply_user = localStorage.getItem('operatorName');
|
||||||
if (!apply_user) {
|
if (!apply_user) {
|
||||||
alert('请先登录');
|
alert('请先登录');
|
||||||
window.location.href = '/home';
|
navigate('/home');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const res = await customSettlementApply({
|
const res = await customSettlementApply({
|
||||||
@@ -813,6 +816,8 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
clearInterval(pollingTimerRef.current);
|
clearInterval(pollingTimerRef.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPaymentMessage('等待支付确认...');
|
||||||
|
|
||||||
pollingTimerRef.current = window.setInterval(async () => {
|
pollingTimerRef.current = window.setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
const res = await checkNativePaymentStatus({
|
const res = await checkNativePaymentStatus({
|
||||||
@@ -844,10 +849,14 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
fetchAddItemBillPdf(physical_exam_id, combinationItemCodes).then((success) => {
|
fetchAddItemBillPdf(physical_exam_id, combinationItemCodes).then((success) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
setPaymentMessage('支付成功,加项单已生成,正在跳转签署...');
|
setPaymentMessage('支付成功,加项单已生成,正在跳转签署...');
|
||||||
// 跳转到签署页面
|
// 延迟跳转,让用户看到消息
|
||||||
onGoToSign?.();
|
setTimeout(() => {
|
||||||
|
onGoToSign?.();
|
||||||
|
setPaymentLoading(false);
|
||||||
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
setPaymentMessage('支付成功,但加项单生成失败');
|
setPaymentMessage('支付成功,但加项单生成失败');
|
||||||
|
setPaymentLoading(false);
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setPaymentMessage(null);
|
setPaymentMessage(null);
|
||||||
@@ -859,6 +868,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
clearInterval(pollingTimerRef.current);
|
clearInterval(pollingTimerRef.current);
|
||||||
pollingTimerRef.current = null;
|
pollingTimerRef.current = null;
|
||||||
}
|
}
|
||||||
|
setPaymentLoading(false);
|
||||||
setPaymentMessage('支付失败');
|
setPaymentMessage('支付失败');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setPaymentMessage(null);
|
setPaymentMessage(null);
|
||||||
@@ -888,7 +898,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setPaymentLoading(true);
|
setPaymentLoading(true);
|
||||||
setPaymentMessage(null);
|
setPaymentMessage('正在发起支付...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const selectedItems = allAddons.filter((item) => selectedIds.has(item.id || item.name));
|
const selectedItems = allAddons.filter((item) => selectedIds.has(item.id || item.name));
|
||||||
@@ -985,6 +995,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setPaymentMessage(res.Message || '生成支付二维码失败');
|
setPaymentMessage(res.Message || '生成支付二维码失败');
|
||||||
|
setPaymentLoading(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 挂账模式:直接查询
|
// 挂账模式:直接查询
|
||||||
@@ -1025,9 +1036,13 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
fetchAddItemBillPdf(physical_exam_id, combinationItemCodes).then((success) => {
|
fetchAddItemBillPdf(physical_exam_id, combinationItemCodes).then((success) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
setPaymentMessage('挂账成功,加项单已生成,正在跳转签署...');
|
setPaymentMessage('挂账成功,加项单已生成,正在跳转签署...');
|
||||||
onGoToSign?.();
|
setTimeout(() => {
|
||||||
|
onGoToSign?.();
|
||||||
|
setPaymentLoading(false);
|
||||||
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
setPaymentMessage('挂账成功,但加项单生成失败');
|
setPaymentMessage('挂账成功,但加项单生成失败');
|
||||||
|
setPaymentLoading(false);
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setPaymentMessage(null);
|
setPaymentMessage(null);
|
||||||
@@ -1035,12 +1050,14 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setPaymentMessage('挂账失败');
|
setPaymentMessage('挂账失败');
|
||||||
|
setPaymentLoading(false);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setPaymentMessage(null);
|
setPaymentMessage(null);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setPaymentMessage(res.Message || '挂账失败');
|
setPaymentMessage(res.Message || '挂账失败');
|
||||||
|
setPaymentLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -1392,6 +1409,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
clearInterval(pollingTimerRef.current);
|
clearInterval(pollingTimerRef.current);
|
||||||
pollingTimerRef.current = null;
|
pollingTimerRef.current = null;
|
||||||
}
|
}
|
||||||
|
setPaymentLoading(false);
|
||||||
setShowQrcodeModal(false);
|
setShowQrcodeModal(false);
|
||||||
setQrcodeUrl(null);
|
setQrcodeUrl(null);
|
||||||
}}
|
}}
|
||||||
@@ -1435,15 +1453,15 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='px-4 py-6 space-y-4'>
|
<div className='px-4 py-1 space-y-4'>
|
||||||
{/* 选中项目信息 */}
|
{/* 选中项目信息 */}
|
||||||
<div className='text-sm text-gray-700'>
|
<div className='text-sm text-gray-700'>
|
||||||
<div className='font-medium mb-2'>选中项目 ({selectedItems.length}项):</div>
|
<div className='font-medium mb-2'>选中项目 ({selectedItems.length}项):</div>
|
||||||
<div className='max-h-32 overflow-y-auto space-y-1'>
|
<div className='max-h-[44px] overflow-y-auto space-y-0.5 pr-1'>
|
||||||
{selectedItems.map((item, idx) => (
|
{selectedItems.map((item, idx) => (
|
||||||
<div key={idx} className='text-xs text-gray-600 flex justify-between'>
|
<div key={idx} className='text-[11px] text-gray-500 flex justify-between'>
|
||||||
<span>{item.name}</span>
|
<span className='truncate mr-2'>{item.name}</span>
|
||||||
<span>¥{parseFloat(item.currentPrice || item.originalPrice || '0').toFixed(2)}</span>
|
<span className='flex-shrink-0 font-mono'>¥{parseFloat(item.currentPrice || item.originalPrice || '0').toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -1577,7 +1595,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
<div className='flex gap-3 pt-2'>
|
<div className='flex gap-3 pt-2'>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setShowCustomSettlementModal(false)}
|
onClick={() => setShowCustomSettlementModal(false)}
|
||||||
className='flex-1 px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700'
|
className='flex-1 px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 text-center items-center justify-center'
|
||||||
>
|
>
|
||||||
{isApprovedOrRejected ? '关闭' : '取消'}
|
{isApprovedOrRejected ? '关闭' : '取消'}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -1586,7 +1604,7 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
onClick={handleSubmitCustomSettlement}
|
onClick={handleSubmitCustomSettlement}
|
||||||
disabled={customSettlementLoading || !customApplyReason.trim()}
|
disabled={customSettlementLoading || !customApplyReason.trim()}
|
||||||
className={cls(
|
className={cls(
|
||||||
'flex-1 px-4 py-2 text-white font-medium',
|
'flex-1 px-4 py-2 text-white font-medium text-center items-center justify-center',
|
||||||
customSettlementLoading || !customApplyReason.trim()
|
customSettlementLoading || !customApplyReason.trim()
|
||||||
? 'bg-gray-400 cursor-not-allowed'
|
? 'bg-gray-400 cursor-not-allowed'
|
||||||
: 'bg-blue-600 hover:bg-blue-700'
|
: 'bg-blue-600 hover:bg-blue-700'
|
||||||
@@ -1602,25 +1620,33 @@ export const ExamAddonPanel = ({ client, onGoToSign }: ExamAddonPanelProps) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
{/* 审核中全屏遮罩 */}
|
{/* 审核中/支付中全屏遮罩 */}
|
||||||
{
|
{
|
||||||
customSettlementStatus?.apply_status === 1 && (
|
(customSettlementStatus?.apply_status === 1 || (paymentLoading && !showQrcodeModal)) && (
|
||||||
<div className='fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50'>
|
<div className='fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50'>
|
||||||
<div className='bg-white rounded-2xl p-6 max-w-md w-full mx-4 shadow-xl'>
|
<div className='bg-white rounded-2xl p-6 max-w-md w-full mx-4 shadow-xl'>
|
||||||
<div className='text-center mb-6'>
|
<div className='text-center mb-6'>
|
||||||
<div className='text-lg font-semibold text-blue-600 mb-2'>审核中...</div>
|
<div className='text-lg font-semibold text-blue-600 mb-2'>
|
||||||
<div className='text-sm text-gray-500 mb-1'>正在等待审核结果,请稍候</div>
|
{customSettlementStatus?.apply_status === 1 ? '审核中...' : '支付处理中...'}
|
||||||
|
</div>
|
||||||
|
<div className='text-sm text-gray-500 mb-1'>
|
||||||
|
{customSettlementStatus?.apply_status === 1
|
||||||
|
? '正在等待审核结果,请稍候'
|
||||||
|
: (paymentMessage || '正在处理,请稍候...')}
|
||||||
|
</div>
|
||||||
<div className='text-xs text-gray-400'>已等待 {waitingSeconds} 秒</div>
|
<div className='text-xs text-gray-400'>已等待 {waitingSeconds} 秒</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-center'>
|
{customSettlementStatus?.apply_status === 1 && (
|
||||||
<Button
|
<div className='flex justify-center'>
|
||||||
onClick={handleCancelCustomSettlement}
|
<Button
|
||||||
disabled={customSettlementLoading}
|
onClick={handleCancelCustomSettlement}
|
||||||
className='px-6 py-2 bg-gray-500 hover:bg-gray-600 text-white disabled:opacity-50'
|
disabled={customSettlementLoading}
|
||||||
>
|
className='px-6 py-2 bg-gray-500 hover:bg-gray-600 text-white disabled:opacity-50'
|
||||||
{customSettlementLoading ? '取消中...' : '取消申请'}
|
>
|
||||||
</Button>
|
{customSettlementLoading ? '取消中...' : '取消申请'}
|
||||||
</div>
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -117,66 +117,66 @@ export const ExamDeliveryPanel = ({ client }: { client: ExamClient }) => {
|
|||||||
{infoLoading && (
|
{infoLoading && (
|
||||||
<div className='mb-3 text-xs text-gray-500'>正在加载地址信息...</div>
|
<div className='mb-3 text-xs text-gray-500'>正在加载地址信息...</div>
|
||||||
)}
|
)}
|
||||||
<div className='grid grid-cols-2 gap-3 mb-3'>
|
<div className='grid grid-cols-2 gap-x-3 gap-y-1.5 mb-2'>
|
||||||
<div>
|
<div>
|
||||||
收件人姓名
|
<span className='text-[11px] text-gray-500'>收件人姓名</span>
|
||||||
<Input
|
<Input
|
||||||
placeholder='请输入收件人姓名'
|
placeholder='请输入收件人姓名'
|
||||||
className='mt-1'
|
className='mt-0.5 h-8 text-xs'
|
||||||
value={addressContact}
|
value={addressContact}
|
||||||
onChange={(e) => setAddressContact(e.target.value)}
|
onChange={(e) => setAddressContact(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
联系电话
|
<span className='text-[11px] text-gray-500'>联系电话</span>
|
||||||
<Input
|
<Input
|
||||||
placeholder='用于快递联系'
|
placeholder='用于快递联系'
|
||||||
className='mt-1'
|
className='mt-0.5 h-8 text-xs'
|
||||||
value={addressMobile}
|
value={addressMobile}
|
||||||
onChange={(e) => setAddressMobile(e.target.value)}
|
onChange={(e) => setAddressMobile(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
省份
|
<span className='text-[11px] text-gray-500'>省份</span>
|
||||||
<Input
|
<Input
|
||||||
placeholder='例如:上海市'
|
placeholder='例如:上海市'
|
||||||
className='mt-1'
|
className='mt-0.5 h-8 text-xs'
|
||||||
value={provinceName}
|
value={provinceName}
|
||||||
onChange={(e) => setProvinceName(e.target.value)}
|
onChange={(e) => setProvinceName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
城市
|
<span className='text-[11px] text-gray-500'>城市</span>
|
||||||
<Input
|
<Input
|
||||||
placeholder='例如:上海市'
|
placeholder='例如:上海市'
|
||||||
className='mt-1'
|
className='mt-0.5 h-8 text-xs'
|
||||||
value={cityName}
|
value={cityName}
|
||||||
onChange={(e) => setCityName(e.target.value)}
|
onChange={(e) => setCityName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
区县
|
<span className='text-[11px] text-gray-500'>区县</span>
|
||||||
<Input
|
<Input
|
||||||
placeholder='例如:浦东新区'
|
placeholder='例如:浦东新区'
|
||||||
className='mt-1'
|
className='mt-0.5 h-8 text-xs'
|
||||||
value={countryName}
|
value={countryName}
|
||||||
onChange={(e) => setCountryName(e.target.value)}
|
onChange={(e) => setCountryName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='col-span-2'>
|
<div className='col-span-2'>
|
||||||
详细地址
|
<span className='text-[11px] text-gray-500'>详细地址</span>
|
||||||
<Input
|
<Input
|
||||||
placeholder='请输入详细寄送地址'
|
placeholder='请输入详细寄送地址'
|
||||||
className='mt-1'
|
className='mt-0.5 h-8 text-xs'
|
||||||
value={addressContent}
|
value={addressContent}
|
||||||
onChange={(e) => setAddressContent(e.target.value)}
|
onChange={(e) => setAddressContent(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='space-y-2'>
|
<div className='space-y-1'>
|
||||||
<div>备注说明</div>
|
<div className='text-[11px] text-gray-500'>备注说明</div>
|
||||||
<textarea
|
<textarea
|
||||||
className='w-full rounded-2xl border px-3 py-2 text-xs outline-none focus:ring-2 focus:ring-gray-200 min-h-[80px]'
|
className='w-full rounded-xl border px-3 py-1.5 text-xs outline-none focus:ring-2 focus:ring-gray-200 min-h-[60px] resize-none'
|
||||||
placeholder='如需多份报告、加急寄送等,请在此备注'
|
placeholder='如需多份报告、加急寄送等,请在此备注'
|
||||||
value={addressRemark}
|
value={addressRemark}
|
||||||
onChange={(e) => setAddressRemark(e.target.value)}
|
onChange={(e) => setAddressRemark(e.target.value)}
|
||||||
@@ -190,12 +190,12 @@ export const ExamDeliveryPanel = ({ client }: { client: ExamClient }) => {
|
|||||||
{saveMessage}
|
{saveMessage}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className='mt-4 flex items-center justify-between text-[11px] text-gray-500'>
|
<div className='mt-2 flex items-center justify-between text-[10px] text-gray-500'>
|
||||||
<div>
|
<div className='truncate mr-2'>
|
||||||
当前客户:<span className='font-medium text-gray-800'>{client.name}</span>(体检号:{client.id})
|
当前客户:<span className='font-medium text-gray-800'>{client.name}</span> ({client.id})
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
className='px-4 py-1.5 text-xs'
|
className='px-4 py-1.5 h-8 text-xs flex-shrink-0'
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const physical_exam_id = Number(client.id);
|
const physical_exam_id = Number(client.id);
|
||||||
if (!physical_exam_id) {
|
if (!physical_exam_id) {
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export const ExamSection = ({
|
|||||||
const hasMore = displayCount < filteredClients.length;
|
const hasMore = displayCount < filteredClients.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='space-y-4'>
|
<div className='space-y-4 h-full overflow-y-auto p-4 pb-10'>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>今日体检进度</CardHeader>
|
<CardHeader>今日体检进度</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
|
|||||||
@@ -418,7 +418,6 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
}, [examId, optionalConfirmed]);
|
}, [examId, optionalConfirmed]);
|
||||||
|
|
||||||
const handlePickFile = () => {
|
const handlePickFile = () => {
|
||||||
// 有可选项目但尚未确认时,禁止拍照并提示先确认项目
|
|
||||||
if (optionalItemList.length > 0 && !optionalConfirmed) {
|
if (optionalItemList.length > 0 && !optionalConfirmed) {
|
||||||
setMessage('请先确认体检项目');
|
setMessage('请先确认体检项目');
|
||||||
setShowOptionalConfirmTip(true);
|
setShowOptionalConfirmTip(true);
|
||||||
@@ -465,24 +464,25 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
|||||||
|
|
||||||
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
if (!file) return;
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setMessage(null);
|
setMessage(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const jpgFile = await convertToJpg(file);
|
const jpgFile = await convertToJpg(file);
|
||||||
setIdCardFile(jpgFile);
|
setIdCardFile(jpgFile);
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
setPreviewImage(event.target?.result as string);
|
setPreviewImage(event.target?.result as string);
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(jpgFile);
|
reader.readAsDataURL(jpgFile);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
alert('err: ' + String(err));
|
||||||
console.error('图片转换失败', err);
|
console.error('图片转换失败', err);
|
||||||
setMessage('图片处理失败,请重试');
|
setMessage('图片处理失败,请重试');
|
||||||
}
|
}
|
||||||
|
|
||||||
e.target.value = '';
|
e.target.value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ export const HomeSection = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='grid grid-cols-2 gap-4'>
|
<div className='grid grid-cols-2 gap-4 pb-4'>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>B1 服务看板</CardHeader>
|
<CardHeader>B1 服务看板</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ interface TopBarProps {
|
|||||||
onLogout?: () => void;
|
onLogout?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TopBar = ({ enableSearch = true, operatorName, onLoginClick, onLogout }: TopBarProps) => {
|
export const TopBar = ({ operatorName, onLoginClick, onLogout }: TopBarProps) => {
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
const [displayName, setDisplayName] = useState(operatorName || '');
|
const [displayName, setDisplayName] = useState(operatorName || '');
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Card, CardContent, CardHeader } from '../ui';
|
import { Card, CardContent } from '../ui';
|
||||||
|
|
||||||
export const SupportSection = () => (
|
export const SupportSection = () => (
|
||||||
<Card>
|
<Card>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import type { ExamClient, ExamModalTab } from '../data/mockData';
|
import type { ExamClient, ExamModalTab } from '../data/mockData';
|
||||||
import { EXAM_TAGS } from '../data/mockData';
|
import { EXAM_TAGS } from '../data/mockData';
|
||||||
@@ -17,13 +18,14 @@ export const ExamPage = () => {
|
|||||||
const [examFilterTags, setExamFilterTags] = useState<Set<(typeof EXAM_TAGS)[number]>>(new Set(['全部']));
|
const [examFilterTags, setExamFilterTags] = useState<Set<(typeof EXAM_TAGS)[number]>>(new Set(['全部']));
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [refreshSeq, setRefreshSeq] = useState(0);
|
const [refreshSeq, setRefreshSeq] = useState(0);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
// 进入页面时获取用户菜单权限
|
// 进入页面时获取用户菜单权限
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const token = localStorage.getItem('accessToken');
|
const token = localStorage.getItem('accessToken');
|
||||||
if (!token) {
|
if (!token) {
|
||||||
// 跳转登录
|
// 跳转登录
|
||||||
window.location.href = '/home';
|
navigate('/home');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getUserOwnedMenus({ app_id: APP_ID })
|
getUserOwnedMenus({ app_id: APP_ID })
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createBrowserRouter, Navigate } from 'react-router-dom';
|
import { createHashRouter, Navigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { MainLayout } from './layouts/MainLayout';
|
import { MainLayout } from './layouts/MainLayout';
|
||||||
import { HomePage } from './pages/HomePage';
|
import { HomePage } from './pages/HomePage';
|
||||||
@@ -6,7 +6,7 @@ import { ExamPage } from './pages/ExamPage';
|
|||||||
import { BookingPage } from './pages/BookingPage';
|
import { BookingPage } from './pages/BookingPage';
|
||||||
import { SupportPage } from './pages/SupportPage';
|
import { SupportPage } from './pages/SupportPage';
|
||||||
|
|
||||||
export const router = createBrowserRouter([
|
export const router = createHashRouter([
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
element: <MainLayout />,
|
element: <MainLayout />,
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ import react from '@vitejs/plugin-react-swc'
|
|||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
base: "./",
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
server: {
|
server: {
|
||||||
allowedHosts: ['ipad.shenynet.com'],
|
allowedHosts: ['ipad.shenynet.com'],
|
||||||
},
|
},
|
||||||
|
resolve: {
|
||||||
|
dedupe: ['react', 'react-dom']
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user