完善加项样式
This commit is contained in:
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user