ContractSelect.tsx 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import React, { useState } from 'react';
  2. import { useQuery } from '@tanstack/react-query';
  3. import { Select, Spin } from 'antd';
  4. import type { SelectProps } from 'antd';
  5. import { InferResponseType } from 'hono/client';
  6. import { hetongClient } from '@/client/api';
  7. // 定义合同数据类型
  8. type ContractItem = InferResponseType<typeof hetongClient[':id']['$get'], 200>;
  9. type ContractListResponse = InferResponseType<typeof hetongClient.$get, 200>;
  10. // 定义组件属性接口
  11. interface ContractSelectProps {
  12. value?: number;
  13. onChange?: (value?: number) => void;
  14. placeholder?: string;
  15. disabled?: boolean;
  16. }
  17. const ContractSelect: React.FC<ContractSelectProps> = ({
  18. value,
  19. onChange,
  20. placeholder = '请选择合同',
  21. disabled = false
  22. }) => {
  23. const [searchValue, setSearchValue] = useState('');
  24. // 使用React Query获取合同列表数据
  25. const { data: contractsData, isLoading, error } = useQuery<ContractListResponse>({
  26. queryKey: ['contracts', searchValue],
  27. queryFn: async () => {
  28. const res = await hetongClient.$get({
  29. query: {
  30. page: 1,
  31. pageSize: 1000,
  32. keyword: searchValue
  33. }
  34. });
  35. if (!res.ok) {
  36. throw new Error('获取合同列表失败');
  37. }
  38. return res.json();
  39. },
  40. });
  41. // 处理搜索输入变化
  42. const handleSearch = (value: string) => {
  43. setSearchValue(value);
  44. };
  45. // 格式化合同数据为Select组件所需的选项格式
  46. const options = contractsData?.data?.map(contract => ({
  47. label: `${contract.contractNumber} - ${contract.client?.name || '未知客户'}`,
  48. value: contract.id,
  49. })) || [];
  50. return (
  51. <div style={{ position: 'relative' }}>
  52. <Select
  53. value={value}
  54. onChange={onChange}
  55. placeholder={placeholder}
  56. disabled={disabled || isLoading}
  57. showSearch
  58. filterOption={false}
  59. onSearch={handleSearch}
  60. style={{ width: '100%' }}
  61. options={options}
  62. notFoundContent={isLoading ? <Spin size="small" /> : '未找到匹配合同'}
  63. />
  64. {isLoading && (
  65. <div style={{
  66. position: 'absolute',
  67. top: '50%',
  68. right: '16px',
  69. transform: 'translateY(-50%)',
  70. pointerEvents: 'none'
  71. }}>
  72. <Spin size="small" />
  73. </div>
  74. )}
  75. </div>
  76. );
  77. };
  78. export default ContractSelect;