优化加项搜索

This commit is contained in:
xianyi
2025-12-29 16:45:36 +08:00
parent 243a6edc2e
commit 1e2163f3ca

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState, useRef } from 'react';
import type { ExamClient } from '../../data/mockData'; import type { ExamClient } from '../../data/mockData';
import { searchPhysicalExamAddItem } from '../../api'; import { searchPhysicalExamAddItem } from '../../api';
@@ -24,10 +24,31 @@ interface ExamAddonPanelProps {
export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => { export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
const [addonList, setAddonList] = useState<AddonItem[]>([]); const [addonList, setAddonList] = useState<AddonItem[]>([]);
const [addonSearch, setAddonSearch] = useState(''); // 防抖:内部输入值(用于显示)
const [addonSearchInput, setAddonSearchInput] = useState('');
// 防抖:实际用于 API 调用的值(延迟更新)
const [debouncedAddonSearch, setDebouncedAddonSearch] = useState('');
const debounceTimerRef = useRef<number | null>(null);
const [addonLoading, setAddonLoading] = useState(false); const [addonLoading, setAddonLoading] = useState(false);
const [addonError, setAddonError] = useState<string | null>(null); const [addonError, setAddonError] = useState<string | null>(null);
// 防抖:当输入值变化时,延迟 0.5 秒后更新 debouncedAddonSearch用于 API 调用)
useEffect(() => {
if (debounceTimerRef.current) {
window.clearTimeout(debounceTimerRef.current);
}
debounceTimerRef.current = window.setTimeout(() => {
setDebouncedAddonSearch(addonSearchInput);
}, 500);
return () => {
if (debounceTimerRef.current) {
window.clearTimeout(debounceTimerRef.current);
}
};
}, [addonSearchInput]);
// 拉取加项列表 // 拉取加项列表
useEffect(() => { useEffect(() => {
const physical_exam_id = Number(client.id); const physical_exam_id = Number(client.id);
@@ -42,7 +63,7 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
try { try {
const res = await searchPhysicalExamAddItem({ const res = await searchPhysicalExamAddItem({
physical_exam_id, physical_exam_id,
item_name: addonSearch.trim() || null, item_name: debouncedAddonSearch.trim() || null,
}); });
if (res.Status === 200 && res.Data?.addItemList) { if (res.Status === 200 && res.Data?.addItemList) {
const list: AddonItem[] = res.Data.addItemList.map((item) => ({ const list: AddonItem[] = res.Data.addItemList.map((item) => ({
@@ -69,7 +90,7 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
} }
}; };
fetchList(); fetchList();
}, [addonSearch, client.id]); }, [debouncedAddonSearch, client.id]);
const allAddons = useMemo(() => addonList, [addonList]); const allAddons = useMemo(() => addonList, [addonList]);
@@ -138,22 +159,31 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
<div> <div>
<div className='flex items-center justify-between mb-2 gap-3'> <div className='flex items-center justify-between mb-2 gap-3'>
<h3 className='text-lg font-semibold text-gray-900'></h3> <h3 className='text-lg font-semibold text-gray-900'></h3>
<div className='w-[260px]'> <div className='w-[260px] flex items-center gap-2'>
<Input <Input
placeholder='搜索 加项名称' placeholder='搜索 加项名称'
value={addonSearch} value={addonSearchInput}
onChange={(e) => setAddonSearch(e.target.value)} onChange={(e) => setAddonSearchInput(e.target.value)}
className='text-sm' className='text-sm flex-1'
/> />
{addonSearchInput && (
<Button
className='px-3 py-1.5 text-xs whitespace-nowrap'
onClick={() => setAddonSearchInput('')}
>
</Button>
)}
</div> </div>
</div> </div>
<div className='text-xs text-gray-600 space-y-1'> {/* <div className='text-xs text-gray-600 space-y-1'>
<div>最多可选 {maxSelect} 项 · 一排 5 个</div> <div>最多可选 {maxSelect} 项 · 一排 5 个</div>
<div>已勾选 {selectedCount} 项,自费加项费用按渠道折扣价结算。</div> <div>已勾选 {selectedCount} 项,自费加项费用按渠道折扣价结算。</div>
</div> </div> */}
</div> </div>
{/* 加项网格 */} {/* 加项网格 */}
<div className='overflow-y-auto max-h-[366px]'>
<div className='grid grid-cols-5 gap-3 min-h-[142px]'> <div className='grid grid-cols-5 gap-3 min-h-[142px]'>
{addonError && ( {addonError && (
<div className='col-span-5 text-xs text-amber-600'>{addonError}</div> <div className='col-span-5 text-xs text-amber-600'>{addonError}</div>
@@ -192,7 +222,7 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
{/* 第二行:标签 */} {/* 第二行:标签 */}
{item.tags && item.tags.length >= 0 && ( {item.tags && item.tags.length >= 0 && (
<div className='flex flex-wrap gap-1 mb-2 pl-6'> <div className='flex flex-wrap gap-1 mb-1 pl-6'>
{item.tags {item.tags
.filter(t => t.type !== 4) // 折扣信息单独显示 .filter(t => t.type !== 4) // 折扣信息单独显示
.map((tag, idx) => ( .map((tag, idx) => (
@@ -207,7 +237,7 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
)} )}
{/* 第三行:价格信息(固定在卡片底部) */} {/* 第三行:价格信息(固定在卡片底部) */}
<div className='mt-auto pt-2'> <div className='mt-auto pt-1'>
<div className='flex flex-col'> <div className='flex flex-col'>
{origPrice >= 0 && origPrice >= currPrice && ( {origPrice >= 0 && origPrice >= currPrice && (
<span className='text-xs text-gray-400 line-through'>¥{origPrice.toFixed(0)}</span> <span className='text-xs text-gray-400 line-through'>¥{origPrice.toFixed(0)}</span>
@@ -224,6 +254,7 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
); );
})} })}
</div> </div>
</div>
{/* 底部汇总和支付 */} {/* 底部汇总和支付 */}
<div className='border-t pt-4 mt-6 flex items-center justify-between'> <div className='border-t pt-4 mt-6 flex items-center justify-between'>