ClientSelect.tsx 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import React, { useState } from 'react';
  2. import { Select, Spin, message } from 'antd';
  3. import { useQuery } from '@tanstack/react-query';
  4. import { clientClient } from '@/client/api';
  5. import type { InferResponseType } from 'hono/client';
  6. import { ClientSchema } from '@/server/modules/clients/client.entity';
  7. // 定义客户选择组件的属性类型
  8. interface ClientSelectProps {
  9. value?: number;
  10. onChange?: (value?: number) => void;
  11. placeholder?: string;
  12. disabled?: boolean;
  13. }
  14. // 提取客户列表响应类型
  15. type ClientListResponse = InferResponseType<typeof clientClient.$get, 200>;
  16. // 客户选择组件实现
  17. const ClientSelect: React.FC<ClientSelectProps> = ({
  18. value,
  19. onChange,
  20. placeholder = '请选择客户',
  21. disabled = false
  22. }): React.ReactElement => {
  23. const [searchValue, setSearchValue] = useState('');
  24. // 使用useQuery获取客户列表数据
  25. const { data: clientsData, isLoading, error } = useQuery<ClientListResponse>({
  26. queryKey: ['clients', 'all'],
  27. queryFn: async () => {
  28. const res = await clientClient.$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. // 格式化客户选项数据
  46. const options = clientsData?.data?.map(client => ({
  47. label: client.companyName,
  48. value: client.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 ClientSelect;