完善加项样式

This commit is contained in:
xianyi
2025-12-30 18:04:31 +08:00
parent 586ce25bdc
commit 0cf99848f0

View File

@@ -3,6 +3,7 @@ import { useEffect, useMemo, useState, useRef } from 'react';
import type { ExamClient } from '../../data/mockData';
import { searchPhysicalExamAddItem, getAddItemCustomerInfo } from '../../api';
import { Button, Input } from '../ui';
import { cls } from '../../utils/cls';
interface AddonTag {
title: string;
@@ -31,7 +32,46 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
const debounceTimerRef = useRef<number | null>(null);
const [addonLoading, setAddonLoading] = useState(false);
const [addonError, setAddonError] = useState<string | null>(null);
// 折扣比例1 = 100%
const [discountRatio, setDiscountRatio] = useState<number>(1);
// 渠道折扣列表
const [channelDiscounts, setChannelDiscounts] = useState<
{ channel_id?: string | null; channel_name?: string | null; discount_rate?: number | null; discount_name?: string | null }[]
>([]);
// 下拉框状态
const [isDiscountDropdownOpen, setIsDiscountDropdownOpen] = useState(false);
const discountDropdownRef = useRef<HTMLDivElement>(null);
// 结算方式:'self' 自费, 'account' 挂账
const [paymentMethod, setPaymentMethod] = useState<'self' | 'account'>('self');
const [isPaymentMethodDropdownOpen, setIsPaymentMethodDropdownOpen] = useState(false);
const paymentMethodDropdownRef = useRef<HTMLDivElement>(null);
// 挂账公司
const [accountCompany, setAccountCompany] = useState<string>('圆和');
const [isAccountCompanyDropdownOpen, setIsAccountCompanyDropdownOpen] = useState(false);
const accountCompanyDropdownRef = useRef<HTMLDivElement>(null);
// 点击外部关闭下拉框
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (discountDropdownRef.current && !discountDropdownRef.current.contains(event.target as Node)) {
setIsDiscountDropdownOpen(false);
}
if (paymentMethodDropdownRef.current && !paymentMethodDropdownRef.current.contains(event.target as Node)) {
setIsPaymentMethodDropdownOpen(false);
}
if (accountCompanyDropdownRef.current && !accountCompanyDropdownRef.current.contains(event.target as Node)) {
setIsAccountCompanyDropdownOpen(false);
}
};
if (isDiscountDropdownOpen || isPaymentMethodDropdownOpen || isAccountCompanyDropdownOpen) {
document.addEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isDiscountDropdownOpen, isPaymentMethodDropdownOpen, isAccountCompanyDropdownOpen]);
// 获取体检加项客户信息(用于拿到渠道折扣等信息)
useEffect(() => {
@@ -42,10 +82,13 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
try {
const res = await getAddItemCustomerInfo({ physical_exam_id });
if (res.Status === 200 && res.Data?.listChannelDiscount && res.Data.listChannelDiscount.length > 0) {
setChannelDiscounts(res.Data.listChannelDiscount);
const rate = res.Data.listChannelDiscount[0]?.discount_rate;
if (typeof rate === 'number' && rate > 0) {
setDiscountRatio(rate);
}
} else {
setChannelDiscounts([]);
}
} catch (err) {
console.error('获取加项客户信息失败', err);
@@ -176,12 +219,103 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
return '渠道价';
};
// 构建折扣选项列表
const discountOptions = useMemo(() => {
const options: Array<{ value: number; label: string }> = [
{ value: 1, label: '100%(无折扣)' },
];
channelDiscounts.forEach((item) => {
const rate = typeof item.discount_rate === 'number' && item.discount_rate > 0 ? item.discount_rate : 1;
const percent = Math.round(rate * 100);
const label = item.discount_name || `${percent}%`;
options.push({ value: rate, label });
});
return options;
}, [channelDiscounts]);
// 获取当前选中的标签
const currentDiscountLabel = useMemo(() => {
const option = discountOptions.find(opt => opt.value === discountRatio);
return option?.label || '100%(无折扣)';
}, [discountRatio, discountOptions]);
// 处理折扣选择
const handleDiscountSelect = (value: number) => {
setDiscountRatio(value);
setIsDiscountDropdownOpen(false);
};
// 处理结算方式选择
const handlePaymentMethodSelect = (value: 'self' | 'account') => {
setPaymentMethod(value);
setIsPaymentMethodDropdownOpen(false);
};
// 处理挂账公司选择
const handleAccountCompanySelect = (value: string) => {
setAccountCompany(value);
setIsAccountCompanyDropdownOpen(false);
};
// 结算方式选项
const paymentMethodOptions: Array<{ value: 'self' | 'account'; label: string }> = [
{ value: 'self', label: '自费' },
{ value: 'account', label: '挂账' },
];
// 挂账公司选项(可以根据实际需求从接口获取)
const accountCompanyOptions: Array<{ value: string; label: string }> = [
{ value: '圆和', label: '圆和' },
];
return (
<div className='space-y-4'>
{/* 标题和说明 */}
<div>
<div className='flex items-center justify-between mb-2 gap-3'>
<h3 className='text-lg font-semibold text-gray-900'></h3>
<div className='flex items-center justify-between mb-3 gap-4'>
<div className='flex items-center gap-2'>
<h3 className='text-lg font-semibold text-gray-900 whitespace-nowrap'></h3>
<div className='flex items-center gap-2 text-xs text-gray-600'>
{/* <span className='whitespace-nowrap'>折扣比例</span> */}
<div ref={discountDropdownRef} className='relative'>
<button
type='button'
onClick={() => setIsDiscountDropdownOpen(!isDiscountDropdownOpen)}
className={cls(
'border-gray-300 rounded-lg px-3 py-1.5 text-xs text-gray-700 bg-white',
'focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500',
'min-w-[140px] flex items-center justify-between gap-2',
'hover:border-gray-400 transition-colors'
)}
>
<span>{currentDiscountLabel}</span>
<span className={cls('text-gray-400 transition-transform text-[10px]', isDiscountDropdownOpen && 'rotate-180')}>
</span>
</button>
{isDiscountDropdownOpen && (
<div className='absolute z-50 w-full mt-1 bg-white border border-gray-300 rounded-lg shadow-lg overflow-hidden'>
{discountOptions.map((option) => (
<button
key={option.value}
type='button'
onClick={() => handleDiscountSelect(option.value)}
className={cls(
'w-full px-3 py-2 text-xs text-left transition-colors',
'hover:bg-gray-50',
discountRatio === option.value && 'bg-blue-50 text-blue-700 font-medium',
discountRatio !== option.value && 'text-gray-700'
)}
>
{option.label}
</button>
))}
</div>
)}
</div>
</div>
</div>
<div className='w-[260px] flex items-center gap-2'>
<Input
placeholder='搜索 加项名称'
@@ -291,14 +425,100 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => {
<span className='text-gray-500 ml-1'> ¥{discount.toFixed(0)}</span>
)}
</div>
<div className='text-xs text-gray-500'>结算方式: 个人支付 ( / )</div>
{/* <div className='text-xs text-gray-500'>结算方式: 个人支付 (微信 / 支付宝)</div> */}
</div>
<div className='flex items-center gap-3'>
{/* 结算方式 */}
<div className='flex items-center gap-2 text-xs text-gray-600'>
<span className='whitespace-nowrap'></span>
<div ref={paymentMethodDropdownRef} className='relative'>
<button
type='button'
onClick={() => setIsPaymentMethodDropdownOpen(!isPaymentMethodDropdownOpen)}
className={cls(
'border border-gray-300 rounded-lg px-3 py-1.5 text-xs text-gray-700 bg-white',
'focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500',
'min-w-[100px] flex items-center justify-between gap-2',
'hover:border-gray-400 transition-colors'
)}
>
<span>{paymentMethodOptions.find(opt => opt.value === paymentMethod)?.label || '自费'}</span>
<span className={cls('text-gray-400 transition-transform text-[10px]', isPaymentMethodDropdownOpen && 'rotate-180')}>
</span>
</button>
{isPaymentMethodDropdownOpen && (
<div className='absolute z-50 w-full bottom-full mb-1 bg-white border border-gray-300 rounded-lg shadow-lg overflow-hidden'>
{paymentMethodOptions.map((option) => (
<button
key={option.value}
type='button'
onClick={() => handlePaymentMethodSelect(option.value)}
className={cls(
'w-full px-3 py-2 text-xs text-left transition-colors',
'hover:bg-gray-50',
paymentMethod === option.value && 'bg-blue-50 text-blue-700 font-medium',
paymentMethod !== option.value && 'text-gray-700'
)}
>
{option.label}
</button>
))}
</div>
)}
</div>
</div>
{/* 挂账公司 */}
{paymentMethod === 'account' && (
<div className='flex items-center gap-2 text-xs text-gray-600'>
<span className='whitespace-nowrap'></span>
<div ref={accountCompanyDropdownRef} className='relative'>
<button
type='button'
onClick={() => setIsAccountCompanyDropdownOpen(!isAccountCompanyDropdownOpen)}
className={cls(
'border border-gray-300 rounded-lg px-3 py-1.5 text-xs text-gray-700 bg-white',
'focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500',
'min-w-[100px] flex items-center justify-between gap-2',
'hover:border-gray-400 transition-colors'
)}
>
<span>{accountCompany}</span>
<span className={cls('text-gray-400 transition-transform text-[10px]', isAccountCompanyDropdownOpen && 'rotate-180')}>
</span>
</button>
{isAccountCompanyDropdownOpen && (
<div className='absolute z-50 w-full bottom-full mb-1 bg-white border border-gray-300 rounded-lg shadow-lg overflow-hidden'>
{accountCompanyOptions.map((option) => (
<button
key={option.value}
type='button'
onClick={() => handleAccountCompanySelect(option.value)}
className={cls(
'w-full px-3 py-2 text-xs text-left transition-colors',
'hover:bg-gray-50',
accountCompany === option.value && 'bg-blue-50 text-blue-700 font-medium',
accountCompany !== option.value && 'text-gray-700'
)}
>
{option.label}
</button>
))}
</div>
)}
</div>
</div>
)}
<Button
className='bg-[#269745] hover:bg-[#269745]/80 rounded-3xl text-white px-6 py-3 text-base font-medium'
disabled={selectedCount === 0}
>
¥{totalCurrent.toFixed(0)}
</Button>
</div>
<Button
className='bg-[#269745] hover:bg-[#269745]/80 rounded-3xl text-white px-6 py-3 text-base font-medium'
disabled={selectedCount === 0}
>
¥{totalCurrent.toFixed(0)}
</Button>
</div>
</div>
);