Kaynağa Gözat

✨ feat(admin): 添加客户选择组件ClientSelect

- 实现客户选择下拉框功能,支持搜索和加载状态显示
- 使用React Query获取客户列表数据,支持分页和关键词搜索
- 定义ClientSelectProps接口,支持value、onChange等常用属性
- 添加加载状态指示器和未找到内容提示
- 格式化客户数据为Select组件所需的选项格式
yourname 8 ay önce
ebeveyn
işleme
6457c66b93
1 değiştirilmiş dosya ile 88 ekleme ve 0 silme
  1. 88 0
      src/client/admin/components/ClientSelect.tsx

+ 88 - 0
src/client/admin/components/ClientSelect.tsx

@@ -0,0 +1,88 @@
+import React, { useState } from 'react';
+import { Select, Spin, message } from 'antd';
+import { useQuery } from '@tanstack/react-query';
+import { clientClient } from '@/client/api';
+import type { InferResponseType } from 'hono/client';
+import { ClientSchema } from '@/server/modules/clients/client.entity';
+
+// 定义客户选择组件的属性类型
+interface ClientSelectProps {
+  value?: number;
+  onChange?: (value?: number) => void;
+  placeholder?: string;
+  disabled?: boolean;
+}
+
+// 提取客户列表响应类型
+type ClientListResponse = InferResponseType<typeof clientClient.$get, 200>;
+
+// 客户选择组件实现
+const ClientSelect: React.FC<ClientSelectProps> = ({
+  value,
+  onChange,
+  placeholder = '请选择客户',
+  disabled = false
+}): React.ReactElement => {
+  const [searchValue, setSearchValue] = useState('');
+
+  // 使用useQuery获取客户列表数据
+  const { data: clientsData, isLoading, error } = useQuery<ClientListResponse>({
+    queryKey: ['clients', 'all'],
+    queryFn: async () => {
+      const res = await clientClient.$get({
+        query: { 
+          page: 1, 
+          pageSize: 1000,
+          keyword: searchValue 
+        }
+      });
+      
+      if (!res.ok) {
+        throw new Error('获取客户列表失败');
+      }
+      
+      return res.json();
+    },
+  });
+
+  // 处理搜索输入变化
+  const handleSearch = (value: string) => {
+    setSearchValue(value);
+  };
+
+  // 格式化客户选项数据
+  const options = clientsData?.data?.map(client => ({
+    label: client.companyName,
+    value: client.id,
+  })) || [];
+
+  return (
+    <div style={{ position: 'relative' }}>
+      <Select
+        value={value}
+        onChange={onChange}
+        placeholder={placeholder}
+        disabled={disabled || isLoading}
+        showSearch
+        filterOption={false}
+        onSearch={handleSearch}
+        style={{ width: '100%' }}
+        options={options}
+        notFoundContent={isLoading ? <Spin size="small" /> : '未找到匹配客户'}
+      />
+      {isLoading && (
+        <div style={{ 
+          position: 'absolute', 
+          top: '50%', 
+          right: '16px', 
+          transform: 'translateY(-50%)',
+          pointerEvents: 'none'
+        }}>
+          <Spin size="small" />
+        </div>
+      )}
+    </div>
+  );
+};
+
+export default ClientSelect;