Ver código fonte

refactor(disability-person-management-ui): 重构 DisabilityPersonCompanyQuery 使用共享组件

- 将原生 HTML 元素替换为 @d8d/shared-ui-components 的 shadcn/ui 组件
- Card, CardHeader, CardTitle, CardContent 替代 div 布局
- Select, Input, Label 替代原生表单元素
- Table 系列组件替代原生 table
- DataTablePagination 替代手动分页实现
- 优化样式和间距,使用响应式网格布局

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 18 horas atrás
pai
commit
076c6156c6

+ 172 - 163
allin-packages/disability-person-management-ui/src/components/DisabilityPersonCompanyQuery.tsx

@@ -1,6 +1,13 @@
 import React, { useState } from 'react';
 import { useQuery, useQueryClient } from '@tanstack/react-query';
 import { disabilityClientManager } from '../api/disabilityClient';
+import { Card, CardContent, CardHeader, CardTitle } from '@d8d/shared-ui-components/components/ui/card';
+import { Input } from '@d8d/shared-ui-components/components/ui/input';
+import { Label } from '@d8d/shared-ui-components/components/ui/label';
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@d8d/shared-ui-components/components/ui/select';
+import { Button } from '@d8d/shared-ui-components/components/ui/button';
+import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@d8d/shared-ui-components/components/ui/table';
+import { DataTablePagination } from '@d8d/shared-ui-components/components/admin/DataTablePagination';
 
 // 残疾人企业查询页面组件
 export const DisabilityPersonCompanyQuery: React.FC = () => {
@@ -75,115 +82,133 @@ export const DisabilityPersonCompanyQuery: React.FC = () => {
   };
 
   return (
-    <div className="p-6" data-testid="disability-person-company-query">
-      <div className="mb-6">
-        <h1 className="text-2xl font-bold mb-4">残疾人企业查询</h1>
-
-        {/* 筛选条件表单 */}
-        <div className="bg-white rounded-lg shadow p-4 mb-4">
-          <div className="grid grid-cols-4 gap-4">
-            <div>
-              <label className="block text-sm font-medium text-gray-700 mb-1">性别</label>
-              <select
-                data-testid="gender-filter"
-                className="w-full border rounded px-3 py-2"
-                value={filters.gender}
-                onChange={(e) => setFilters({ ...filters, gender: e.target.value, page: 1 })}
+    <div className="p-6 space-y-6" data-testid="disability-person-company-query">
+      {/* 筛选条件卡片 */}
+      <Card>
+        <CardHeader>
+          <CardTitle>残疾人企业查询</CardTitle>
+        </CardHeader>
+        <CardContent>
+          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
+            {/* 性别筛选 */}
+            <div className="space-y-2">
+              <Label htmlFor="gender-filter">性别</Label>
+              <Select
+                value={filters.gender || 'all'}
+                onValueChange={(value) => setFilters({ ...filters, gender: value === 'all' ? '' : value, page: 1 })}
               >
-                <option value="">全部</option>
-                <option value="男">男</option>
-                <option value="女">女</option>
-              </select>
+                <SelectTrigger id="gender-filter" data-testid="gender-filter" className="w-full">
+                  <SelectValue placeholder="全部" />
+                </SelectTrigger>
+                <SelectContent>
+                  <SelectItem value="all">全部</SelectItem>
+                  <SelectItem value="男">男</SelectItem>
+                  <SelectItem value="女">女</SelectItem>
+                </SelectContent>
+              </Select>
             </div>
 
-            <div>
-              <label className="block text-sm font-medium text-gray-700 mb-1">残疾类别</label>
-              <select
-                data-testid="disability-type-filter"
-                className="w-full border rounded px-3 py-2"
-                value={filters.disabilityType}
-                onChange={(e) => setFilters({ ...filters, disabilityType: e.target.value, page: 1 })}
+            {/* 残疾类别筛选 */}
+            <div className="space-y-2">
+              <Label htmlFor="disability-type-filter">残疾类别</Label>
+              <Select
+                value={filters.disabilityType || 'all'}
+                onValueChange={(value) => setFilters({ ...filters, disabilityType: value === 'all' ? '' : value, page: 1 })}
               >
-                <option value="">全部</option>
-                <option value="视力残疾">视力残疾</option>
-                <option value="听力残疾">听力残疾</option>
-                <option value="言语残疾">言语残疾</option>
-                <option value="肢体残疾">肢体残疾</option>
-                <option value="智力残疾">智力残疾</option>
-                <option value="精神残疾">精神残疾</option>
-              </select>
+                <SelectTrigger id="disability-type-filter" data-testid="disability-type-filter" className="w-full">
+                  <SelectValue placeholder="全部" />
+                </SelectTrigger>
+                <SelectContent>
+                  <SelectItem value="all">全部</SelectItem>
+                  <SelectItem value="视力残疾">视力残疾</SelectItem>
+                  <SelectItem value="听力残疾">听力残疾</SelectItem>
+                  <SelectItem value="言语残疾">言语残疾</SelectItem>
+                  <SelectItem value="肢体残疾">肢体残疾</SelectItem>
+                  <SelectItem value="智力残疾">智力残疾</SelectItem>
+                  <SelectItem value="精神残疾">精神残疾</SelectItem>
+                </SelectContent>
+              </Select>
             </div>
 
-            <div>
-              <label className="block text-sm font-medium text-gray-700 mb-1">残疾等级</label>
-              <select
-                data-testid="disability-level-filter"
-                className="w-full border rounded px-3 py-2"
-                value={filters.disabilityLevel}
-                onChange={(e) => setFilters({ ...filters, disabilityLevel: e.target.value, page: 1 })}
+            {/* 残疾等级筛选 */}
+            <div className="space-y-2">
+              <Label htmlFor="disability-level-filter">残疾等级</Label>
+              <Select
+                value={filters.disabilityLevel || 'all'}
+                onValueChange={(value) => setFilters({ ...filters, disabilityLevel: value === 'all' ? '' : value, page: 1 })}
               >
-                <option value="">全部</option>
-                <option value="一级">一级</option>
-                <option value="二级">二级</option>
-                <option value="三级">三级</option>
-                <option value="四级">四级</option>
-              </select>
+                <SelectTrigger id="disability-level-filter" data-testid="disability-level-filter" className="w-full">
+                  <SelectValue placeholder="全部" />
+                </SelectTrigger>
+                <SelectContent>
+                  <SelectItem value="all">全部</SelectItem>
+                  <SelectItem value="一级">一级</SelectItem>
+                  <SelectItem value="二级">二级</SelectItem>
+                  <SelectItem value="三级">三级</SelectItem>
+                  <SelectItem value="四级">四级</SelectItem>
+                </SelectContent>
+              </Select>
             </div>
 
-            <div>
-              <label className="block text-sm font-medium text-gray-700 mb-1">最小年龄</label>
-              <input
+            {/* 最小年龄筛选 */}
+            <div className="space-y-2">
+              <Label htmlFor="min-age-filter">最小年龄</Label>
+              <Input
+                id="min-age-filter"
                 data-testid="min-age-filter"
                 type="number"
-                className="w-full border rounded px-3 py-2"
                 value={filters.minAge}
                 onChange={(e) => setFilters({ ...filters, minAge: e.target.value, page: 1 })}
                 placeholder="最小年龄"
               />
             </div>
 
-            <div>
-              <label className="block text-sm font-medium text-gray-700 mb-1">最大年龄</label>
-              <input
+            {/* 最大年龄筛选 */}
+            <div className="space-y-2">
+              <Label htmlFor="max-age-filter">最大年龄</Label>
+              <Input
+                id="max-age-filter"
                 data-testid="max-age-filter"
                 type="number"
-                className="w-full border rounded px-3 py-2"
                 value={filters.maxAge}
                 onChange={(e) => setFilters({ ...filters, maxAge: e.target.value, page: 1 })}
                 placeholder="最大年龄"
               />
             </div>
 
-            <div>
-              <label className="block text-sm font-medium text-gray-700 mb-1">市</label>
-              <input
+            {/* 市筛选 */}
+            <div className="space-y-2">
+              <Label htmlFor="city-filter">市</Label>
+              <Input
+                id="city-filter"
                 data-testid="city-filter"
                 type="text"
-                className="w-full border rounded px-3 py-2"
                 value={filters.city}
                 onChange={(e) => setFilters({ ...filters, city: e.target.value, page: 1 })}
                 placeholder="输入市级"
               />
             </div>
 
-            <div>
-              <label className="block text-sm font-medium text-gray-700 mb-1">区</label>
-              <input
+            {/* 区筛选 */}
+            <div className="space-y-2">
+              <Label htmlFor="district-filter">区</Label>
+              <Input
+                id="district-filter"
                 data-testid="district-filter"
                 type="text"
-                className="w-full border rounded px-3 py-2"
                 value={filters.district}
                 onChange={(e) => setFilters({ ...filters, district: e.target.value, page: 1 })}
                 placeholder="输入区级"
               />
             </div>
 
-            <div>
-              <label className="block text-sm font-medium text-gray-700 mb-1">残疾证号</label>
-              <input
+            {/* 残疾证号筛选 */}
+            <div className="space-y-2">
+              <Label htmlFor="disability-id-filter">残疾证号</Label>
+              <Input
+                id="disability-id-filter"
                 data-testid="disability-id-filter"
                 type="text"
-                className="w-full border rounded px-3 py-2"
                 value={filters.disabilityId}
                 onChange={(e) => setFilters({ ...filters, disabilityId: e.target.value, page: 1 })}
                 placeholder="输入残疾证号"
@@ -193,125 +218,109 @@ export const DisabilityPersonCompanyQuery: React.FC = () => {
 
           {/* 操作按钮 */}
           <div className="flex gap-2 mt-4">
-            <button
+            <Button
               data-testid="reset-button"
               onClick={handleReset}
-              className="px-4 py-2 border rounded hover:bg-gray-50"
+              variant="outline"
             >
               重置
-            </button>
-            <button
+            </Button>
+            <Button
               data-testid="search-button"
               onClick={handleSearch}
-              className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
             >
               查询
-            </button>
-          </div>
-        </div>
-      </div>
-
-      {/* 数据表格 */}
-      <div className="bg-white rounded-lg shadow overflow-hidden">
-        {isLoading ? (
-          <div className="p-4 text-center" data-testid="loading-state">加载中...</div>
-        ) : error ? (
-          <div className="p-4 text-center text-red-600" data-testid="error-state">
-            加载失败: {error instanceof Error ? error.message : '未知错误'}
+            </Button>
           </div>
-        ) : (
-          <>
-            <table className="min-w-full divide-y divide-gray-200" data-testid="results-table">
-              <thead className="bg-gray-50">
-                <tr>
-                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">姓名</th>
-                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">残疾类别</th>
-                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">残疾等级</th>
-                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">公司</th>
-                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">户籍</th>
-                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">残疾证号</th>
-                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">区</th>
-                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">市</th>
-                </tr>
-              </thead>
-              <tbody className="bg-white divide-y divide-gray-200">
-                {!data?.data || data.data.length === 0 ? (
-                  <tr data-testid="no-data-row">
-                    <td colSpan={8} className="px-6 py-4 text-center text-gray-500">
-                      暂无数据
-                    </td>
-                  </tr>
-                ) : (
-                  data.data.map((item: any) => (
-                    <tr key={`${item.personId}-${item.orderId}`}>
-                      <td className="px-6 py-4 whitespace-nowrap">{item.name}</td>
-                      <td className="px-6 py-4 whitespace-nowrap">{item.disabilityType}</td>
-                      <td className="px-6 py-4 whitespace-nowrap">{item.disabilityLevel}</td>
-                      <td className="px-6 py-4 whitespace-nowrap">{item.companyName}</td>
-                      <td className="px-6 py-4 whitespace-nowrap">{item.city} {item.district || ''}</td>
-                      <td className="px-6 py-4 whitespace-nowrap">{item.disabilityId}</td>
-                      <td className="px-6 py-4 whitespace-nowrap">{item.district || '-'}</td>
-                      <td className="px-6 py-4 whitespace-nowrap">{item.city}</td>
-                    </tr>
-                  ))
-                )}
-              </tbody>
-            </table>
+        </CardContent>
+      </Card>
 
-            {/* 分页 */}
-            <div className="bg-gray-50 px-6 py-4 border-t border-gray-200 flex items-center justify-between" data-testid="pagination">
-              <div className="text-sm text-gray-700" data-testid="total-records">
-                共 {data?.total || 0} 条记录
-              </div>
-              <div className="flex gap-2">
-                <button
-                  data-testid="prev-page-button"
-                  onClick={() => setFilters({ ...filters, page: Math.max(1, filters.page - 1) })}
-                  disabled={filters.page === 1}
-                  className="px-4 py-2 border rounded hover:bg-gray-50 disabled:opacity-50"
-                >
-                  上一页
-                </button>
-                <span className="px-4 py-2" data-testid="current-page">
-                  第 {filters.page} 页
-                </span>
-                <button
-                  data-testid="next-page-button"
-                  onClick={() => setFilters({ ...filters, page: filters.page + 1 })}
-                  disabled={!data?.data || data?.data.length < filters.limit}
-                  className="px-4 py-2 border rounded hover:bg-gray-50 disabled:opacity-50"
-                >
-                  下一页
-                </button>
-              </div>
+      {/* 数据表格卡片 */}
+      <Card>
+        <CardContent className="p-0">
+          {isLoading ? (
+            <div className="p-4 text-center" data-testid="loading-state">加载中...</div>
+          ) : error ? (
+            <div className="p-4 text-center text-destructive" data-testid="error-state">
+              加载失败: {error instanceof Error ? error.message : '未知错误'}
             </div>
-          </>
-        )}
-      </div>
+          ) : (
+            <>
+              <Table data-testid="results-table">
+                <TableHeader>
+                  <TableRow>
+                    <TableHead>姓名</TableHead>
+                    <TableHead>残疾类别</TableHead>
+                    <TableHead>残疾等级</TableHead>
+                    <TableHead>公司</TableHead>
+                    <TableHead>户籍</TableHead>
+                    <TableHead>残疾证号</TableHead>
+                    <TableHead>区</TableHead>
+                    <TableHead>市</TableHead>
+                  </TableRow>
+                </TableHeader>
+                <TableBody>
+                  {!data?.data || data.data.length === 0 ? (
+                    <TableRow data-testid="no-data-row">
+                      <TableCell colSpan={8} className="text-center text-muted-foreground">
+                        暂无数据
+                      </TableCell>
+                    </TableRow>
+                  ) : (
+                    data.data.map((item: any) => (
+                      <TableRow key={`${item.personId}-${item.orderId}`}>
+                        <TableCell>{item.name}</TableCell>
+                        <TableCell>{item.disabilityType}</TableCell>
+                        <TableCell>{item.disabilityLevel}</TableCell>
+                        <TableCell>{item.companyName}</TableCell>
+                        <TableCell>{item.city} {item.district || ''}</TableCell>
+                        <TableCell>{item.disabilityId}</TableCell>
+                        <TableCell>{item.district || '-'}</TableCell>
+                        <TableCell>{item.city}</TableCell>
+                      </TableRow>
+                    ))
+                  )}
+                </TableBody>
+              </Table>
+
+              {/* 分页 */}
+              {data.total > 0 && (
+                <div className="p-4">
+                  <DataTablePagination
+                    currentPage={filters.page}
+                    pageSize={filters.limit}
+                    totalCount={data.total}
+                    onPageChange={(page, pageSize) => setFilters({ ...filters, page, limit: pageSize })}
+                  />
+                </div>
+              )}
+            </>
+          )}
+        </CardContent>
+      </Card>
 
       {/* 操作按钮区域 - 占位符功能,待实现 */}
-      <div className="mt-4 flex gap-2" data-testid="action-buttons">
-        <button
-          className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 opacity-50 cursor-not-allowed"
-          title="此功能待实现"
+      <div className="flex gap-2" data-testid="action-buttons">
+        <Button
           disabled
+          title="此功能待实现"
         >
           新增
-        </button>
-        <button
-          className="px-4 py-2 border rounded hover:bg-gray-50 opacity-50 cursor-not-allowed"
-          title="此功能待实现"
+        </Button>
+        <Button
+          variant="outline"
           disabled
+          title="此功能待实现"
         >
           编辑
-        </button>
-        <button
-          className="px-4 py-2 border rounded hover:bg-gray-50 opacity-50 cursor-not-allowed"
-          title="此功能待实现"
+        </Button>
+        <Button
+          variant="outline"
           disabled
+          title="此功能待实现"
         >
           删除
-        </button>
+        </Button>
       </div>
     </div>
   );