فهرست منبع

新增点位管理 修复系统配置一些按钮和搜索框的样式 改用antd

pm 5 ماه پیش
والد
کامیت
9c63fca7c3

+ 34 - 46
src/components/DepartmentManagement.tsx

@@ -1,8 +1,7 @@
 import React, { useState, useEffect, useRef } from 'react';
 import { Plus, Search, RefreshCw, ChevronRight, ChevronDown, Edit2, Trash2 } from 'lucide-react';
 import { Button } from './ui/button';
-import { Input } from './ui/input';
-import { Label } from './ui/label';
+import { Button as AntButton, Input, Space } from 'antd';
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
 import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './ui/table';
 import PermissionWrapper from './PermissionWrapper';
@@ -226,67 +225,56 @@ export default function DepartmentManagement() {
     <div className="space-y-4">
       {/* 搜索栏 */}
       <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-5">
-        <div className="flex items-center justify-between gap-4">
+        <div className="flex items-center justify-between gap-4 flex-wrap">
           {/* 搜索输入框 */}
-          <div className="flex items-center gap-3 flex-1 max-w-md">
-            <Label htmlFor="dept-name-search" className="text-sm font-medium text-gray-700 whitespace-nowrap">
-              部门名称
-            </Label>
-            <Input
-              id="dept-name-search"
-              placeholder="请输入部门名称"
-              value={queryParams.name || ''}
-              onChange={(e) => setQueryParams(prev => ({ ...prev, name: e.target.value }))}
-              onKeyDown={(e) => e.key === 'Enter' && handleQuery()}
-              className="h-10 bg-gray-50 border-gray-200 focus:bg-white focus:border-blue-400 focus:ring-2 focus:ring-blue-100 transition-all"
-            />
+          <div className="flex items-center gap-3 flex-wrap flex-1">
+            <div className="flex items-center gap-3">
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">部门名称:</label>
+              <Input
+                value={queryParams.name || ''}
+                onChange={(e) => setQueryParams(prev => ({ ...prev, name: e.target.value }))}
+                onPressEnter={handleQuery}
+                placeholder="请输入部门名称"
+                style={{ width: 192 }}
+                allowClear
+              />
+            </div>
           </div>
 
           {/* 操作按钮组 */}
-          <div className="flex items-center gap-3">
-            <button
+          <Space>
+            <AntButton
+              type="primary"
+              icon={<Search className="w-4 h-4" />}
               onClick={handleQuery}
-              className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
             >
-              <Search className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">搜索</span>
-            </button>
+              搜索
+            </AntButton>
             
-            <button
+            <AntButton
+              icon={<RefreshCw className="w-4 h-4" />}
               onClick={resetQuery}
-              className="flex items-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 hover:border-gray-400 transition-all duration-300"
             >
-              <RefreshCw className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">重置</span>
-            </button>
+              重置
+            </AntButton>
             
             <PermissionWrapper permission="system:dept:create">
-              <button
+              <AntButton
+                type="primary"
+                icon={<Plus className="w-4 h-4" />}
                 onClick={() => openForm('create')}
-                className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
               >
-                <Plus className="w-4 h-4" strokeWidth={2.5} />
-                <span className="text-sm">新增</span>
-              </button>
+                新增
+              </AntButton>
             </PermissionWrapper>
             
-            <button
+            <AntButton
+              icon={isExpandAll ? <ChevronDown className="w-4 h-4" /> : <ChevronRight className="w-4 h-4" />}
               onClick={toggleExpandAll}
-              className="flex items-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 hover:border-gray-400 transition-all duration-300"
             >
-              {isExpandAll ? (
-                <>
-                  <ChevronDown className="w-4 h-4" strokeWidth={2.5} />
-                  <span className="text-sm">折叠</span>
-                </>
-              ) : (
-                <>
-                  <ChevronRight className="w-4 h-4" strokeWidth={2.5} />
-                  <span className="text-sm">展开</span>
-                </>
-              )}
-            </button>
-          </div>
+              {isExpandAll ? '折叠' : '展开'}
+            </AntButton>
+          </Space>
         </div>
       </div>
 

+ 37 - 50
src/components/DictTypeManagement.tsx

@@ -5,17 +5,15 @@ import { dictDataApi, DictDataVO, PageParam as DictDataPageParam } from '../api/
 import { toast } from 'sonner';
 import { formatDateTimeFull } from '../utils/formatTime';
 import { getIntDictOptions, DICT_TYPE, getDictLabel } from '../utils/dict';
-import { Modal, Tag, Space, Table, Button as AntButton, Input as AntInput, Pagination } from 'antd';
+import { Modal, Tag, Space, Table, Button, Input, Pagination } from 'antd';
 import { ExclamationCircleOutlined } from '@ant-design/icons';
-import { Button } from './ui/button';
-import { Input } from './ui/input';
-import { Label } from './ui/label';
+import { Button as UIButton } from './ui/button';
 import { Table as UITable, TableBody, TableCell, TableHead, TableHeader, TableRow } from './ui/table';
 import DictTypeForm, { DictTypeFormRef } from './DictTypeForm';
 import DictDataForm, { DictDataFormRef } from './DictDataForm';
 import type { ColumnsType } from 'antd/es/table';
 
-const { Search: AntSearch } = AntInput;
+const { Search: AntSearch } = Input;
 
 export default function DictTypeManagement() {
   const [loading, setLoading] = useState(true);
@@ -325,60 +323,54 @@ export default function DictTypeManagement() {
           {/* 搜索输入框 */}
           <div className="flex items-center gap-3 flex-wrap flex-1">
             <div className="flex items-center gap-3">
-              <Label htmlFor="dict-name-search" className="text-sm font-medium text-gray-700 whitespace-nowrap">
-                字典名称
-              </Label>
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">字典名称:</label>
               <Input
-                id="dict-name-search"
-                placeholder="请输入字典名称"
                 value={queryParams.name || ''}
                 onChange={(e) => setQueryParams(prev => ({ ...prev, name: e.target.value }))}
-                onKeyDown={(e) => e.key === 'Enter' && handleQuery()}
-                className="h-10 bg-gray-50 border-gray-200 focus:bg-white focus:border-blue-400 focus:ring-2 focus:ring-blue-100 transition-all w-60"
+                onPressEnter={handleQuery}
+                placeholder="请输入字典名称"
+                style={{ width: 192 }}
+                allowClear
               />
             </div>
-
             <div className="flex items-center gap-3">
-              <Label htmlFor="dict-type-search" className="text-sm font-medium text-gray-700 whitespace-nowrap">
-                字典类型
-              </Label>
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">字典类型:</label>
               <Input
-                id="dict-type-search"
-                placeholder="请输入字典类型"
                 value={queryParams.type || ''}
                 onChange={(e) => setQueryParams(prev => ({ ...prev, type: e.target.value }))}
-                onKeyDown={(e) => e.key === 'Enter' && handleQuery()}
-                className="h-10 bg-gray-50 border-gray-200 focus:bg-white focus:border-blue-400 focus:ring-2 focus:ring-blue-100 transition-all w-60"
+                onPressEnter={handleQuery}
+                placeholder="请输入字典类型"
+                style={{ width: 192 }}
+                allowClear
               />
             </div>
           </div>
 
           {/* 操作按钮组 */}
-          <div className="flex items-center gap-3">
-            <button
+          <Space>
+            <Button
+              type="primary"
+              icon={<Search className="w-4 h-4" />}
               onClick={handleQuery}
-              className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
             >
-              <Search className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">搜索</span>
-            </button>
+              搜索
+            </Button>
             
-            <button
+            <Button
+              icon={<RefreshCw className="w-4 h-4" />}
               onClick={resetQuery}
-              className="flex items-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 hover:border-gray-400 transition-all duration-300"
             >
-              <RefreshCw className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">重置</span>
-            </button>
+              重置
+            </Button>
             
-            <button
+            <Button
+              type="primary"
+              icon={<Plus className="w-4 h-4" />}
               onClick={() => openForm('create')}
-              className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
             >
-              <Plus className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">新增</span>
-            </button>
-          </div>
+              新增
+            </Button>
+          </Space>
         </div>
       </div>
 
@@ -429,7 +421,7 @@ export default function DictTypeManagement() {
                   </TableCell>
                   <TableCell>
                     <div className="flex items-center gap-2 justify-center">
-                      <Button
+                      <UIButton
                         variant="ghost"
                         size="sm"
                         onClick={() => openForm('update', row.id)}
@@ -437,8 +429,8 @@ export default function DictTypeManagement() {
                       >
                         <Edit2 className="w-4 h-4 mr-1" />
                         修改
-                      </Button>
-                      <Button
+                      </UIButton>
+                      <UIButton
                         variant="ghost"
                         size="sm"
                         onClick={() => openDictDataModal(row)}
@@ -446,8 +438,8 @@ export default function DictTypeManagement() {
                       >
                         <Eye className="w-4 h-4 mr-1" />
                         数据
-                      </Button>
-                      <Button
+                      </UIButton>
+                      <UIButton
                         variant="ghost"
                         size="sm"
                         onClick={() => handleDelete(row.id!, row.name)}
@@ -455,7 +447,7 @@ export default function DictTypeManagement() {
                       >
                         <Trash2 className="w-4 h-4 mr-1" />
                         删除
-                      </Button>
+                      </UIButton>
                     </div>
                   </TableCell>
                 </TableRow>
@@ -474,8 +466,6 @@ export default function DictTypeManagement() {
             </div>
             <div className="flex gap-2">
               <Button
-                variant="outline"
-                size="sm"
                 onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo! - 1 })}
                 disabled={queryParams.pageNo! <= 1}
               >
@@ -485,8 +475,6 @@ export default function DictTypeManagement() {
                 {queryParams.pageNo} / {Math.ceil(total / queryParams.pageSize!) || 1}
               </span>
               <Button
-                variant="outline"
-                size="sm"
                 onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo! + 1 })}
                 disabled={queryParams.pageNo! >= Math.ceil(total / queryParams.pageSize!)}
               >
@@ -523,13 +511,13 @@ export default function DictTypeManagement() {
               style={{ width: 300 }}
               enterButton
             />
-            <Button
+            <UIButton
               onClick={() => openDictDataForm('create')}
               className="flex items-center gap-2"
             >
               <Plus className="w-4 h-4" />
               新增字典项
-            </Button>
+            </UIButton>
           </div>
 
           {/* 表格 */}
@@ -567,7 +555,6 @@ export default function DictTypeManagement() {
         onSuccess={async () => {
           if (currentDictType) {
             await loadDictDataFull(currentDictType.type, dictDataPageParams);
-            await loadDictDataPreview(currentDictType.type);
           }
         }} 
       />

+ 45 - 59
src/components/MenuManagement.tsx

@@ -9,14 +9,12 @@ import {
   ShoppingBag, Package, Database, PieChart, BarChart3, 
   Grid3x3, List, Link2, Key, Wrench, Monitor, Server
 } from 'lucide-react';
-import { Modal } from 'antd';
+import { Modal, Button, Input, Space } from 'antd';
 import { ExclamationCircleOutlined } from '@ant-design/icons';
 import { menuApi, MenuVO, MenuQueryParams, MenuType, MenuStatus } from '../api/Menu';
 import { handleTree, TreeNode } from '../utils/tree';
 import { toast } from 'sonner';
-import { Button } from './ui/button';
-import { Input } from './ui/input';
-import { Label } from './ui/label';
+import { Button as UIButton } from './ui/button';
 import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './ui/table';
 import { Switch } from './ui/switch';
 import MenuForm, { MenuFormRef } from './MenuForm';
@@ -373,30 +371,30 @@ export default function MenuManagement() {
           </TableCell>
           <TableCell>
             <div className="flex items-center gap-2 justify-center">
-              <Button
+              <UIButton
                 variant="ghost"
                 size="sm"
                 onClick={() => openForm('update', node.id)}
                 className="h-8 px-2"
               >
                 <Edit2 className="w-4 h-4" />
-              </Button>
-              <Button
+              </UIButton>
+              <UIButton
                 variant="ghost"
                 size="sm"
                 onClick={() => openForm('create', undefined, node.id)}
                 className="h-8 px-2"
               >
                 <Plus className="w-4 h-4" />
-              </Button>
-              <Button
+              </UIButton>
+              <UIButton
                 variant="ghost"
                 size="sm"
                 onClick={() => handleDelete(node.id!, node.name)}
                 className="h-8 px-2 text-red-600 hover:text-red-700"
               >
                 <Trash2 className="w-4 h-4" />
-              </Button>
+              </UIButton>
             </div>
           </TableCell>
         </TableRow>
@@ -413,73 +411,61 @@ export default function MenuManagement() {
     <div className="space-y-4">
       {/* 搜索栏 */}
       <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-5">
-        <div className="flex items-center justify-between gap-4">
+        <div className="flex items-center justify-between gap-4 flex-wrap">
           {/* 搜索输入框 */}
-          <div className="flex items-center gap-3 flex-1 max-w-md">
-            <Label htmlFor="menu-name-search" className="text-sm font-medium text-gray-700 whitespace-nowrap">
-              菜单名称
-            </Label>
-            <Input
-              id="menu-name-search"
-              placeholder="请输入菜单名称"
-              value={queryParams.name || ''}
-              onChange={(e) => setQueryParams(prev => ({ ...prev, name: e.target.value }))}
-              onKeyDown={(e) => e.key === 'Enter' && handleQuery()}
-              className="h-10 bg-gray-50 border-gray-200 focus:bg-white focus:border-blue-400 focus:ring-2 focus:ring-blue-100 transition-all"
-            />
+          <div className="flex items-center gap-3 flex-wrap flex-1">
+            <div className="flex items-center gap-3">
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">菜单名称:</label>
+              <Input
+                value={queryParams.name || ''}
+                onChange={(e) => setQueryParams(prev => ({ ...prev, name: e.target.value }))}
+                onPressEnter={handleQuery}
+                placeholder="请输入菜单名称"
+                style={{ width: 192 }}
+                allowClear
+              />
+            </div>
           </div>
 
           {/* 操作按钮组 */}
-          <div className="flex items-center gap-3">
-            <button
+          <Space>
+            <Button
+              type="primary"
+              icon={<Search className="w-4 h-4" />}
               onClick={handleQuery}
-              className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
             >
-              <Search className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">搜索</span>
-            </button>
+              搜索
+            </Button>
             
-            <button
+            <Button
+              icon={<RefreshCw className="w-4 h-4" />}
               onClick={resetQuery}
-              className="flex items-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 hover:border-gray-400 transition-all duration-300"
             >
-              <RefreshCw className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">重置</span>
-            </button>
+              重置
+            </Button>
             
-            <button
+            <Button
+              type="primary"
+              icon={<Plus className="w-4 h-4" />}
               onClick={() => openForm('create')}
-              className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
             >
-              <Plus className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">新增</span>
-            </button>
+              新增
+            </Button>
             
-            <button
+            <Button
+              icon={isExpandAll ? <ChevronDown className="w-4 h-4" /> : <ChevronRight className="w-4 h-4" />}
               onClick={toggleExpandAll}
-              className="flex items-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 hover:border-gray-400 transition-all duration-300"
             >
-              {isExpandAll ? (
-                <>
-                  <ChevronDown className="w-4 h-4" strokeWidth={2.5} />
-                  <span className="text-sm">折叠</span>
-                </>
-              ) : (
-                <>
-                  <ChevronRight className="w-4 h-4" strokeWidth={2.5} />
-                  <span className="text-sm">展开</span>
-                </>
-              )}
-            </button>
+              {isExpandAll ? '折叠' : '展开'}
+            </Button>
             
-            <button
+            <Button
+              icon={<RefreshCw className="w-4 h-4" />}
               onClick={refreshMenu}
-              className="flex items-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 hover:border-gray-400 transition-all duration-300"
             >
-              <RefreshCw className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">刷新缓存</span>
-            </button>
-          </div>
+              刷新缓存
+            </Button>
+          </Space>
         </div>
       </div>
 

+ 37 - 50
src/components/PostManagement.tsx

@@ -3,11 +3,9 @@ import { Search, Plus, RefreshCw, Edit2, Trash2, Download } from 'lucide-react';
 import { postApi, PostVO, PostStatus, PageParam } from '../api/Post';
 import { toast } from 'sonner';
 import { formatDateTimeFull } from '../utils/formatTime';
-import { Modal } from 'antd';
+import { Modal, Button, Input, Space } from 'antd';
 import { ExclamationCircleOutlined } from '@ant-design/icons';
-import { Button } from './ui/button';
-import { Input } from './ui/input';
-import { Label } from './ui/label';
+import { Button as UIButton } from './ui/button';
 import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './ui/table';
 import PostForm, { PostFormRef } from './PostForm';
 
@@ -195,69 +193,62 @@ export default function PostManagement() {
           {/* 搜索输入框 */}
           <div className="flex items-center gap-3 flex-wrap flex-1">
             <div className="flex items-center gap-3">
-              <Label htmlFor="post-name-search" className="text-sm font-medium text-gray-700 whitespace-nowrap">
-                岗位名称
-              </Label>
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">岗位名称:</label>
               <Input
-                id="post-name-search"
-                placeholder="请输入岗位名称"
                 value={queryParams.name || ''}
                 onChange={(e) => setQueryParams(prev => ({ ...prev, name: e.target.value }))}
-                onKeyDown={(e) => e.key === 'Enter' && handleQuery()}
-                className="h-10 bg-gray-50 border-gray-200 focus:bg-white focus:border-blue-400 focus:ring-2 focus:ring-blue-100 transition-all w-60"
+                onPressEnter={handleQuery}
+                placeholder="请输入岗位名称"
+                style={{ width: 192 }}
+                allowClear
               />
             </div>
-
             <div className="flex items-center gap-3">
-              <Label htmlFor="post-code-search" className="text-sm font-medium text-gray-700 whitespace-nowrap">
-                岗位编码
-              </Label>
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">岗位编码:</label>
               <Input
-                id="post-code-search"
-                placeholder="请输入岗位编码"
                 value={queryParams.code || ''}
                 onChange={(e) => setQueryParams(prev => ({ ...prev, code: e.target.value }))}
-                onKeyDown={(e) => e.key === 'Enter' && handleQuery()}
-                className="h-10 bg-gray-50 border-gray-200 focus:bg-white focus:border-blue-400 focus:ring-2 focus:ring-blue-100 transition-all w-60"
+                onPressEnter={handleQuery}
+                placeholder="请输入岗位编码"
+                style={{ width: 192 }}
+                allowClear
               />
             </div>
           </div>
 
           {/* 操作按钮组 */}
-          <div className="flex items-center gap-3">
-            <button
+          <Space>
+            <Button
+              type="primary"
+              icon={<Search className="w-4 h-4" />}
               onClick={handleQuery}
-              className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
             >
-              <Search className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">搜索</span>
-            </button>
+              搜索
+            </Button>
             
-            <button
+            <Button
+              icon={<RefreshCw className="w-4 h-4" />}
               onClick={resetQuery}
-              className="flex items-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 hover:border-gray-400 transition-all duration-300"
             >
-              <RefreshCw className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">重置</span>
-            </button>
+              重置
+            </Button>
             
-            <button
+            <Button
+              type="primary"
+              icon={<Plus className="w-4 h-4" />}
               onClick={() => openForm('create')}
-              className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
             >
-              <Plus className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">新增</span>
-            </button>
+              新增
+            </Button>
             
-            <button
+            <Button
+              icon={<Download className="w-4 h-4" />}
               onClick={handleExport}
-              disabled={exportLoading}
-              className="flex items-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 hover:border-gray-400 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed"
+              loading={exportLoading}
             >
-              <Download className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">{exportLoading ? '导出中...' : '导出'}</span>
-            </button>
-          </div>
+              导出
+            </Button>
+          </Space>
         </div>
       </div>
 
@@ -307,22 +298,22 @@ export default function PostManagement() {
                   </TableCell>
                   <TableCell>
                     <div className="flex items-center gap-2 justify-center">
-                      <Button
+                      <UIButton
                         variant="ghost"
                         size="sm"
                         onClick={() => openForm('update', row.id)}
                         className="h-8 px-2"
                       >
                         <Edit2 className="w-4 h-4" />
-                      </Button>
-                      <Button
+                      </UIButton>
+                      <UIButton
                         variant="ghost"
                         size="sm"
                         onClick={() => handleDelete(row.id!, row.name)}
                         className="h-8 px-2 text-red-600 hover:text-red-700"
                       >
                         <Trash2 className="w-4 h-4" />
-                      </Button>
+                      </UIButton>
                     </div>
                   </TableCell>
                 </TableRow>
@@ -341,8 +332,6 @@ export default function PostManagement() {
             </div>
             <div className="flex gap-2">
               <Button
-                variant="outline"
-                size="sm"
                 onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo! - 1 })}
                 disabled={queryParams.pageNo! <= 1}
               >
@@ -352,8 +341,6 @@ export default function PostManagement() {
                 {queryParams.pageNo} / {Math.ceil(total / queryParams.pageSize!) || 1}
               </span>
               <Button
-                variant="outline"
-                size="sm"
                 onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo! + 1 })}
                 disabled={queryParams.pageNo! >= Math.ceil(total / queryParams.pageSize!)}
               >

+ 41 - 55
src/components/RoleManagement.tsx

@@ -4,11 +4,9 @@ import { roleApi, RoleVO, PageParam } from '../api/Role';
 import { toast } from 'sonner';
 import { formatDateTimeFull } from '../utils/formatTime';
 import { getIntDictOptions, DICT_TYPE, getDictLabel } from '../utils/dict';
-import { Modal } from 'antd';
+import { Modal, Button, Input, Space } from 'antd';
 import { ExclamationCircleOutlined } from '@ant-design/icons';
-import { Button } from './ui/button';
-import { Input } from './ui/input';
-import { Label } from './ui/label';
+import { Button as UIButton } from './ui/button';
 import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './ui/table';
 import RoleForm, { RoleFormRef } from './RoleForm';
 import RoleAssignMenuForm, { RoleAssignMenuFormRef } from './RoleAssignMenuForm';
@@ -185,70 +183,62 @@ export default function RoleManagement() {
           {/* 搜索输入框 */}
           <div className="flex items-center gap-3 flex-wrap flex-1">
             <div className="flex items-center gap-3">
-              <Label htmlFor="role-name-search" className="text-sm font-medium text-gray-700 whitespace-nowrap">
-                角色名称
-              </Label>
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">角色名称:</label>
               <Input
-                id="role-name-search"
-                placeholder="请输入角色名称"
                 value={queryParams.name || ''}
                 onChange={(e) => setQueryParams(prev => ({ ...prev, name: e.target.value }))}
-                onKeyDown={(e) => e.key === 'Enter' && handleQuery()}
-                className="h-10 bg-gray-50 border-gray-200 focus:bg-white focus:border-blue-400 focus:ring-2 focus:ring-blue-100 transition-all w-60"
+                onPressEnter={handleQuery}
+                placeholder="请输入角色名称"
+                style={{ width: 192 }}
+                allowClear
               />
             </div>
-
             <div className="flex items-center gap-3">
-              <Label htmlFor="role-code-search" className="text-sm font-medium text-gray-700 whitespace-nowrap">
-                角色标识
-              </Label>
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">角色标识:</label>
               <Input
-                id="role-code-search"
-                placeholder="请输入角色标识"
                 value={queryParams.code || ''}
                 onChange={(e) => setQueryParams(prev => ({ ...prev, code: e.target.value }))}
-                onKeyDown={(e) => e.key === 'Enter' && handleQuery()}
-                className="h-10 bg-gray-50 border-gray-200 focus:bg-white focus:border-blue-400 focus:ring-2 focus:ring-blue-100 transition-all w-60"
+                onPressEnter={handleQuery}
+                placeholder="请输入角色标识"
+                style={{ width: 192 }}
+                allowClear
               />
             </div>
-
           </div>
 
           {/* 操作按钮组 */}
-          <div className="flex items-center gap-3">
-            <button
+          <Space>
+            <Button
+              type="primary"
+              icon={<Search className="w-4 h-4" />}
               onClick={handleQuery}
-              className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
             >
-              <Search className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">搜索</span>
-            </button>
+              搜索
+            </Button>
             
-            <button
+            <Button
+              icon={<RefreshCw className="w-4 h-4" />}
               onClick={resetQuery}
-              className="flex items-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 hover:border-gray-400 transition-all duration-300"
             >
-              <RefreshCw className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">重置</span>
-            </button>
+              重置
+            </Button>
             
-            <button
+            <Button
+              type="primary"
+              icon={<Plus className="w-4 h-4" />}
               onClick={() => openForm('create')}
-              className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
             >
-              <Plus className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">新增</span>
-            </button>
+              新增
+            </Button>
             
-            <button
+            <Button
+              icon={<Download className="w-4 h-4" />}
               onClick={handleExport}
-              disabled={exportLoading}
-              className="flex items-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-xl hover:bg-gray-50 hover:border-gray-400 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed"
+              loading={exportLoading}
             >
-              <Download className="w-4 h-4" strokeWidth={2.5} />
-              <span className="text-sm">{exportLoading ? '导出中...' : '导出'}</span>
-            </button>
-          </div>
+              导出
+            </Button>
+          </Space>
         </div>
       </div>
 
@@ -302,7 +292,7 @@ export default function RoleManagement() {
                   </TableCell>
                   <TableCell>
                     <div className="flex items-center gap-2 justify-center">
-                      <Button
+                      <UIButton
                         variant="ghost"
                         size="sm"
                         onClick={() => openForm('update', row.id)}
@@ -310,8 +300,8 @@ export default function RoleManagement() {
                       >
                         <Edit2 className="w-4 h-4" />
                         <span className="ml-1">编辑</span>
-                      </Button>
-                      <Button
+                      </UIButton>
+                      <UIButton
                         variant="ghost"
                         size="sm"
                         onClick={() => openAssignMenuForm(row)}
@@ -319,8 +309,8 @@ export default function RoleManagement() {
                       >
                         <Settings className="w-4 h-4" />
                         <span className="ml-1">菜单权限</span>
-                      </Button>
-                      <Button
+                      </UIButton>
+                      <UIButton
                         variant="ghost"
                         size="sm"
                         onClick={() => openDataPermissionForm(row)}
@@ -328,8 +318,8 @@ export default function RoleManagement() {
                       >
                         <Shield className="w-4 h-4" />
                         <span className="ml-1">数据权限</span>
-                      </Button>
-                      <Button
+                      </UIButton>
+                      <UIButton
                         variant="ghost"
                         size="sm"
                         onClick={() => handleDelete(row.id!, row.name)}
@@ -337,7 +327,7 @@ export default function RoleManagement() {
                       >
                         <Trash2 className="w-4 h-4" />
                         <span className="ml-1">删除</span>
-                      </Button>
+                      </UIButton>
                     </div>
                   </TableCell>
                 </TableRow>
@@ -356,8 +346,6 @@ export default function RoleManagement() {
             </div>
             <div className="flex gap-2">
               <Button
-                variant="outline"
-                size="sm"
                 onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo! - 1 })}
                 disabled={queryParams.pageNo! <= 1}
               >
@@ -367,8 +355,6 @@ export default function RoleManagement() {
                 {queryParams.pageNo} / {Math.ceil(total / queryParams.pageSize!) || 1}
               </span>
               <Button
-                variant="outline"
-                size="sm"
                 onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo! + 1 })}
                 disabled={queryParams.pageNo! >= Math.ceil(total / queryParams.pageSize!)}
               >

+ 383 - 0
src/components/SegregationPointForm.tsx

@@ -0,0 +1,383 @@
+import React, { useState, useImperativeHandle, forwardRef, useEffect } from 'react';
+import { Modal, Form, Input, Select, TreeSelect, Upload, Image, Row, Col, message } from 'antd';
+import { PlusOutlined, UploadOutlined } from '@ant-design/icons';
+import type { UploadFile } from 'antd/es/upload/interface';
+import { segregationPointApi, SegregationPointVO } from '../api/spm/index';
+import { marsDeptApi } from '../api/marsdept/index';
+import { lotoStationApi } from '../api/lotoStation/index';
+import { rfidApi } from '../api/rfid/index';
+import { loginApi } from '../api/Login';
+import { handleTree } from '../utils/tree';
+import { getStrDictOptions, DICT_TYPE } from '../utils/dict';
+import { toast } from 'sonner';
+
+interface SegregationPointFormProps {
+  onSuccess?: () => void;
+}
+
+export interface SegregationPointFormRef {
+  open: (type: string, id?: number) => void;
+}
+
+const SegregationPointForm = forwardRef<SegregationPointFormRef, SegregationPointFormProps>(
+  ({ onSuccess }, ref) => {
+    const [dialogVisible, setDialogVisible] = useState(false);
+    const [dialogTitle, setDialogTitle] = useState('');
+    const [formLoading, setFormLoading] = useState(false);
+    const [formType, setFormType] = useState<'create' | 'update'>('create');
+    const [form] = Form.useForm();
+    const [selectedImageIndex, setSelectedImageIndex] = useState(-1);
+    const [imageMap, setImageMap] = useState<Record<number, string>>({});
+
+    // 下拉选项数据
+    const [deptOptions, setDeptOptions] = useState<any[]>([]);
+    const [lotoOptions, setLotoOptions] = useState<Array<{ label: string; value: number }>>([]);
+    const [rfidTokenData, setRfidTokenData] = useState<Array<{ label: string; value: number }>>([]);
+    const powerTypeOptions = getStrDictOptions(DICT_TYPE.POWER_TYPE);
+
+    // 打开弹窗
+    const open = async (type: string, id?: number) => {
+      setDialogVisible(true);
+      setDialogTitle(type === 'create' ? '新增隔离点' : '编辑隔离点');
+      setFormType(type as 'create' | 'update');
+      form.resetFields();
+      setSelectedImageIndex(-1);
+      setImageMap({});
+
+      // 加载下拉选项数据
+      await loadOptionsData();
+
+      // 修改时,设置数据
+      if (id) {
+        setFormLoading(true);
+        try {
+          const response = await segregationPointApi.selectIsIsolationPointById(id);
+          const data = response?.data || response;
+          form.setFieldsValue({
+            pointId: data.pointId,
+            pointName: data.pointName,
+            pointIcon: data.pointIcon,
+            pointPicture: data.pointPicture,
+            rfidId: data.rfidId,
+            workstationId: data.workstationId,
+            lotoId: data.lotoId,
+            powerType: data.powerType,
+            pointSerialNumber: data.pointSerialNumber,
+            remark: data.remark,
+          });
+
+          // 获取隔离点图标
+          await loadIsolationIcons(data.pointIcon);
+        } catch (error: any) {
+          toast.error(error.message || '获取隔离点详情失败');
+        } finally {
+          setFormLoading(false);
+        }
+      } else {
+        // 新增时也加载图标
+        await loadIsolationIcons();
+      }
+    };
+
+    // 加载下拉选项数据
+    const loadOptionsData = async () => {
+      try {
+        // 获取岗位数据
+        const deptRes = await marsDeptApi.listMarsDept({ pageNo: 1, pageSize: -1 });
+        const deptTreeData = handleTree(deptRes.list, 'id', 'parentId');
+        setDeptOptions(convertToTreeSelectData(deptTreeData, 'workstationName'));
+
+        // 获取锁定站数据
+        const lotoRes = await lotoStationApi.listLoto({ pageNo: 1, pageSize: -1 });
+        setLotoOptions(lotoRes.list.map(item => ({
+          value: item.id!,
+          label: item.lotoName,
+        })));
+
+        // 获取RFID Token数据
+        const rfidRes = await rfidApi.getIsRfidTokenPage({ pageNo: 1, pageSize: -1 });
+        setRfidTokenData(rfidRes.list.map(record => ({
+          value: record.id!,
+          label: record.rfid,
+        })));
+      } catch (error) {
+        console.error('加载选项数据失败:', error);
+      }
+    };
+
+    // 加载隔离点图标
+    const loadIsolationIcons = async (selectedIcon?: string) => {
+      try {
+        const sysAttrKey1 = 'sys.icon_set.isolation';
+        const iconRes = await loginApi.getSystemAttributeByKey(sysAttrKey1);
+        const values = (iconRes as any).sysAttrValue?.split(',').map((v: string) => v.trim()) || [];
+        
+        const iconPromises = values.map((value: string) => 
+          loginApi.getSystemAttributeByKey(value)
+        );
+        const iconResponses = await Promise.all(iconPromises);
+        
+        const newImageMap: Record<number, string> = {};
+        iconResponses.forEach((response: any, index: number) => {
+          newImageMap[index] = response.sysAttrValue || '';
+        });
+        setImageMap(newImageMap);
+
+        // 确定选中的图片索引
+        if (selectedIcon) {
+          const index = getImageIndexByIcon(selectedIcon, newImageMap);
+          setSelectedImageIndex(index);
+        }
+      } catch (error) {
+        console.error('加载隔离点图标失败:', error);
+      }
+    };
+
+    // 获取图片索引
+    const getImageIndexByIcon = (iconUrl: string, imageMap: Record<number, string>): number => {
+      for (const [index, imageUrl] of Object.entries(imageMap)) {
+        if (imageUrl === iconUrl) {
+          return Number(index);
+        }
+      }
+      return -1;
+    };
+
+    // 转换树形数据为 TreeSelect 格式
+    const convertToTreeSelectData = (treeData: any[], labelKey: string): any[] => {
+      return treeData.map(item => ({
+        title: item[labelKey],
+        value: item.id,
+        key: item.id,
+        children: item.children ? convertToTreeSelectData(item.children, labelKey) : undefined,
+      }));
+    };
+
+    // 选择隔离点图标
+    const selectIcon = (imageUrl: string, index: number) => {
+      form.setFieldsValue({ pointIcon: imageUrl });
+      setSelectedImageIndex(index);
+    };
+
+    useImperativeHandle(ref, () => ({
+      open,
+    }));
+
+    // 提交表单
+    const submitForm = async () => {
+      try {
+        const values = await form.validateFields();
+        
+        setFormLoading(true);
+        try {
+          const data: SegregationPointVO = {
+            pointId: values.pointId,
+            pointName: values.pointName,
+            pointIcon: values.pointIcon,
+            pointPicture: values.pointPicture,
+            rfidId: values.rfidId,
+            workstationId: values.workstationId,
+            lotoId: values.lotoId,
+            powerType: values.powerType,
+            pointSerialNumber: values.pointSerialNumber,
+            remark: values.remark,
+          };
+
+          if (formType === 'create') {
+            await segregationPointApi.addinsertIsIsolationPoint(data);
+            toast.success('新增成功');
+          } else {
+            await segregationPointApi.updateIsIsolationPoint(data);
+            toast.success('修改成功');
+          }
+          setDialogVisible(false);
+          onSuccess?.();
+        } catch (error: any) {
+          toast.error(error.message || '操作失败');
+        } finally {
+          setFormLoading(false);
+        }
+      } catch (error) {
+        // 表单验证失败
+      }
+    };
+
+    // 图片上传配置
+    const uploadProps = {
+      name: 'file',
+      action: '/api/upload', // 需要根据实际接口调整
+      listType: 'picture-card' as const,
+      maxCount: 1,
+      beforeUpload: (file: File) => {
+        const isImage = file.type.startsWith('image/');
+        if (!isImage) {
+          message.error('只能上传图片文件!');
+          return false;
+        }
+        const isLt2M = file.size / 1024 / 1024 < 2;
+        if (!isLt2M) {
+          message.error('图片大小不能超过 2MB!');
+          return false;
+        }
+        return false; // 阻止自动上传,需要手动处理
+      },
+      onChange: (info: any) => {
+        if (info.file.status === 'done') {
+          // 上传成功,设置图片URL
+          form.setFieldsValue({ pointPicture: info.file.response?.url || info.file.url });
+        }
+      },
+    };
+
+    return (
+      <Modal
+        title={dialogTitle}
+        open={dialogVisible}
+        onCancel={() => setDialogVisible(false)}
+        onOk={submitForm}
+        confirmLoading={formLoading}
+        width={960}
+        destroyOnClose
+      >
+        <Form
+          form={form}
+          labelCol={{ span: 6 }}
+          wrapperCol={{ span: 18 }}
+          layout="horizontal"
+        >
+          <Row gutter={16}>
+            <Col span={12}>
+              <Form.Item
+                label="隔离点名称"
+                name="pointName"
+                rules={[{ required: true, message: '隔离点名称不能为空' }]}
+              >
+                <Input placeholder="请输入隔离点名称" />
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item
+                label="隔离点NFC"
+                name="rfidId"
+                rules={[{ required: true, message: '隔离点NFC不能为空' }]}
+              >
+                <Select placeholder="请选择隔离点NFC" options={rfidTokenData} />
+              </Form.Item>
+            </Col>
+          </Row>
+
+          <Row gutter={16}>
+            <Col span={12}>
+              <Form.Item
+                label="锁定站"
+                name="lotoId"
+                rules={[{ required: true, message: '锁定站不能为空' }]}
+              >
+                <Select placeholder="请选择锁定站" options={lotoOptions} />
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item
+                label="岗位"
+                name="workstationId"
+                rules={[{ required: true, message: '岗位不能为空' }]}
+              >
+                <TreeSelect
+                  treeData={deptOptions}
+                  placeholder="选择岗位"
+                  style={{ width: '100%' }}
+                />
+              </Form.Item>
+            </Col>
+          </Row>
+
+          <Row gutter={16}>
+            <Col span={12}>
+              <Form.Item
+                label="能量源"
+                name="powerType"
+              >
+                <Select placeholder="请选择能量源" options={powerTypeOptions} />
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item
+                label="隔离点序列号"
+                name="pointSerialNumber"
+              >
+                <Input placeholder="请输入隔离点序列号" />
+              </Form.Item>
+            </Col>
+          </Row>
+
+          <Row gutter={16}>
+            <Col span={12}>
+              <Form.Item
+                label="作用"
+                name="remark"
+              >
+                <Input placeholder="请输入作用" />
+              </Form.Item>
+            </Col>
+          </Row>
+
+          <Row gutter={16}>
+            <Col span={12}>
+              <Form.Item
+                label="隔离点图标"
+                name="pointIcon"
+              >
+                <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>
+                  {Object.entries(imageMap).map(([index, imageUrl]) => (
+                    <div
+                      key={index}
+                      onClick={() => selectIcon(imageUrl, Number(index))}
+                      style={{
+                        width: '55px',
+                        height: '55px',
+                        cursor: 'pointer',
+                        border: selectedImageIndex === Number(index) ? '2px solid rgb(2, 86, 255)' : '2px solid transparent',
+                        padding: '2px',
+                        transition: 'border-color 0.3s',
+                      }}
+                    >
+                      <img src={imageUrl} alt="Isolation Icon" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
+                    </div>
+                  ))}
+                </div>
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item
+                label="隔离点图片"
+                name="pointPicture"
+              >
+                <Upload {...uploadProps}>
+                  {form.getFieldValue('pointPicture') ? (
+                    <Image
+                      src={form.getFieldValue('pointPicture')}
+                      width={100}
+                      height={100}
+                      style={{ objectFit: 'cover' }}
+                      preview={false}
+                    />
+                  ) : (
+                    <div>
+                      <PlusOutlined />
+                      <div style={{ marginTop: 8 }}>上传</div>
+                    </div>
+                  )}
+                </Upload>
+              </Form.Item>
+            </Col>
+          </Row>
+        </Form>
+      </Modal>
+    );
+  }
+);
+
+SegregationPointForm.displayName = 'SegregationPointForm';
+
+export default SegregationPointForm;
+

+ 439 - 0
src/components/SegregationPointManagement.tsx

@@ -0,0 +1,439 @@
+import React, { useState, useEffect, useRef } from 'react';
+import { Plus, Search, RefreshCw } from 'lucide-react';
+import { segregationPointApi, SegregationPointVO, PageParam } from '../api/spm/index';
+import { marsDeptApi, MarsDeptVO } from '../api/marsdept/index';
+import { lotoStationApi, LotoStationVO } from '../api/lotoStation/index';
+import { technologyApi, TechnologyVO } from '../api/technology/index';
+import { toast } from 'sonner';
+import { Modal, Table, Input, Button, Select, TreeSelect, Space, Image, Switch } from 'antd';
+import { ExclamationCircleOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
+import type { ColumnsType } from 'antd/es/table';
+import { handleTree } from '../utils/tree';
+import { getStrDictOptions, DICT_TYPE } from '../utils/dict';
+import SegregationPointForm, { SegregationPointFormRef } from './SegregationPointForm';
+
+interface SegregationPointManagementProps {
+  subMenu?: string;
+}
+
+export default function SegregationPointManagement({ subMenu }: SegregationPointManagementProps) {
+  const [loading, setLoading] = useState(true);
+  const [list, setList] = useState<SegregationPointVO[]>([]);
+  const [total, setTotal] = useState(0);
+  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
+  const [queryParams, setQueryParams] = useState<PageParam>({
+    current: 1,
+    size: 10,
+    pointName: undefined,
+    workstationId: undefined,
+    machineryId: undefined,
+    lotoId: undefined,
+    powerType: undefined,
+  });
+
+  // 下拉选项数据
+  const [deptOptions, setDeptOptions] = useState<any[]>([]);
+  const [machineryOptions, setMachineryOptions] = useState<any[]>([]);
+  const [lotoOptions, setLotoOptions] = useState<Array<{ label: string; value: number }>>([]);
+  const powerTypeOptions = getStrDictOptions(DICT_TYPE.POWER_TYPE);
+
+  const formRef = useRef<SegregationPointFormRef>(null);
+
+  // 获取隔离点列表
+  const getList = async (params?: PageParam) => {
+    const currentParams = params || queryParams;
+    setLoading(true);
+    try {
+      console.log('SegregationPointManagement: 开始获取隔离点列表', currentParams);
+      const response = await segregationPointApi.getIsIsolationPointPage(currentParams);
+      console.log('SegregationPointManagement: API 响应', response);
+      
+      // 处理响应数据
+      let data;
+      if (response && typeof response === 'object') {
+        // 如果响应有 data 属性,使用 data;否则直接使用 response
+        if ('data' in response && response.data) {
+          data = response.data;
+        } else if ('list' in response || 'total' in response) {
+          data = response;
+        } else {
+          data = response;
+        }
+      } else {
+        data = response;
+      }
+      
+      setList(data?.list || []);
+      setTotal(data?.total || 0);
+      console.log('SegregationPointManagement: 设置列表数据', { list: data?.list || [], total: data?.total || 0 });
+    } catch (error: any) {
+      console.error('SegregationPointManagement: 获取列表失败', error);
+      toast.error(error.message || '获取隔离点列表失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    getList();
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [queryParams.current, queryParams.size]);
+
+  // 初始化数据
+  useEffect(() => {
+    const initData = async () => {
+      try {
+        // 获取岗位数据
+        const deptRes = await marsDeptApi.listMarsDept({ pageNo: 1, pageSize: -1 });
+        const deptTreeData = handleTree(deptRes.list, 'id', 'parentId');
+        setDeptOptions(convertToTreeSelectData(deptTreeData, 'workstationName'));
+
+        // 获取锁定站数据
+        const lotoRes = await lotoStationApi.listLoto({ pageNo: 1, pageSize: -1 });
+        setLotoOptions(lotoRes.list.map(item => ({
+          value: item.id!,
+          label: item.lotoName,
+        })));
+
+        // 获取设备/工艺数据
+        const techRes = await technologyApi.listTechnology({ pageNo: 1, pageSize: -1 });
+        const techData = techRes.list.filter(item => item.machineryType === '工艺');
+        const techTreeData = handleTree(techData, 'id', 'parentId');
+        setMachineryOptions(convertToTreeSelectData(techTreeData, 'machineryName'));
+      } catch (error) {
+        console.error('初始化数据失败:', error);
+      }
+    };
+    initData();
+  }, []);
+
+  // 转换树形数据为 TreeSelect 格式
+  const convertToTreeSelectData = (treeData: any[], labelKey: string): any[] => {
+    return treeData.map(item => ({
+      title: item[labelKey],
+      value: item.id,
+      key: item.id,
+      children: item.children ? convertToTreeSelectData(item.children, labelKey) : undefined,
+    }));
+  };
+
+  // 搜索
+  const handleQuery = () => {
+    setQueryParams({ ...queryParams, current: 1 });
+    getList({ ...queryParams, current: 1 });
+  };
+
+  // 重置搜索
+  const resetQuery = () => {
+    const resetParams: PageParam = {
+      current: 1,
+      size: 10,
+      pointName: undefined,
+      workstationId: undefined,
+      machineryId: undefined,
+      lotoId: undefined,
+      powerType: undefined,
+    };
+    setQueryParams(resetParams);
+    getList(resetParams);
+  };
+
+  // 打开表单
+  const openForm = (type: string, id?: number) => {
+    formRef.current?.open(type, id);
+  };
+
+  // 删除隔离点
+  const handleDelete = async (id?: number) => {
+    const pointIds = id ? [id] : selectedRowKeys.map(key => Number(key));
+    
+    Modal.confirm({
+      title: '确认删除',
+      icon: <ExclamationCircleOutlined />,
+      content: `确定要删除选中的 ${pointIds.length} 条数据吗?`,
+      okText: '确定删除',
+      okType: 'danger',
+      cancelText: '取消',
+      onOk: async () => {
+        try {
+          await segregationPointApi.deleteIsIsolationPointByPointIds(pointIds);
+          toast.success('删除成功');
+          setSelectedRowKeys([]);
+          await getList();
+        } catch (error: any) {
+          toast.error(error.message || '删除失败');
+        }
+      },
+    });
+  };
+
+  // 表格列配置
+  const columns: ColumnsType<SegregationPointVO> = [
+    {
+      title: '隔离点编号',
+      dataIndex: 'id',
+      width: 100,
+      align: 'center',
+    },
+    {
+      title: '隔离点名称',
+      dataIndex: 'pointName',
+      align: 'center',
+    },
+    {
+      title: '隔离点图标',
+      dataIndex: 'pointIcon',
+      width: 100,
+      align: 'center',
+      render: (text: string) => {
+        if (text) {
+          return <Image src={text} width={50} height={50} style={{ objectFit: 'cover' }} />;
+        }
+        return '-';
+      },
+    },
+    {
+      title: '开关状态',
+      dataIndex: 'switchStatus',
+      width: 100,
+      align: 'center',
+      render: (status: number | string | null) => {
+        if (status === null || status === undefined) {
+          return '-';
+        }
+        const isChecked = String(status) === '1';
+        return (
+          <Switch
+            checked={isChecked}
+            checkedChildren="ON"
+            unCheckedChildren="OFF"
+            disabled
+            style={{ pointerEvents: 'none' }}
+          />
+        );
+      },
+    },
+    {
+      title: '隔离点NFC',
+      dataIndex: 'pointNfc',
+      align: 'center',
+    },
+    {
+      title: '岗位',
+      dataIndex: 'workstationName',
+      align: 'center',
+    },
+    {
+      title: '设备/工艺',
+      dataIndex: 'machineryName',
+      align: 'center',
+    },
+    {
+      title: '锁定站',
+      dataIndex: 'lotoName',
+      align: 'center',
+    },
+    {
+      title: '隔离点序列号',
+      dataIndex: 'pointSerialNumber',
+      align: 'center',
+    },
+    {
+      title: '作用',
+      dataIndex: 'remark',
+      align: 'center',
+    },
+    {
+      title: '隔离点图片',
+      dataIndex: 'pointPicture',
+      width: 100,
+      align: 'center',
+      render: (text: string) => {
+        if (text) {
+          return <Image src={text} width={50} height={50} style={{ objectFit: 'cover' }} />;
+        }
+        return '-';
+      },
+    },
+    {
+      title: '能量源',
+      dataIndex: 'powerType',
+      align: 'center',
+      render: (value: string) => {
+        const option = powerTypeOptions.find(opt => opt.value === value);
+        return option ? option.label : value || '-';
+      },
+    },
+    {
+      title: '操作',
+      width: 120,
+      align: 'center',
+      fixed: 'right',
+      render: (_: any, record: SegregationPointVO) => (
+        <Space>
+          <Button
+            type="link"
+            icon={<EditOutlined />}
+            onClick={() => openForm('update', record.pointId)}
+            title="编辑"
+          />
+          <Button
+            type="link"
+            danger
+            icon={<DeleteOutlined />}
+            onClick={() => handleDelete(record.pointId)}
+            title="删除"
+          />
+        </Space>
+      ),
+    },
+  ];
+
+  return (
+    <div className="p-6 space-y-4">
+      {/* 搜索栏 */}
+      <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-5">
+        <div className="flex items-center justify-between gap-4 flex-wrap">
+          {/* 搜索输入框 */}
+          <div className="flex items-center gap-3 flex-wrap flex-1">
+            <div className="flex items-center gap-3">
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">隔离点名称:</label>
+              <Input
+                value={queryParams.pointName}
+                onChange={(e) => setQueryParams({ ...queryParams, pointName: e.target.value })}
+                onPressEnter={handleQuery}
+                placeholder="请输入隔离点名称"
+                style={{ width: 192 }}
+                allowClear
+              />
+            </div>
+            <div className="flex items-center gap-3">
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">岗位:</label>
+              <TreeSelect
+                value={queryParams.workstationId}
+                onChange={(value) => setQueryParams({ ...queryParams, workstationId: value })}
+                treeData={deptOptions}
+                placeholder="选择岗位"
+                allowClear
+                style={{ width: 192 }}
+              />
+            </div>
+            <div className="flex items-center gap-3">
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">设备/工艺:</label>
+              <TreeSelect
+                value={queryParams.machineryId}
+                onChange={(value) => setQueryParams({ ...queryParams, machineryId: value })}
+                treeData={machineryOptions}
+                placeholder="选择设备/工艺"
+                allowClear
+                style={{ width: 192 }}
+              />
+            </div>
+            <div className="flex items-center gap-3">
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">锁定站:</label>
+              <Select
+                value={queryParams.lotoId}
+                onChange={(value) => setQueryParams({ ...queryParams, lotoId: value })}
+                placeholder="请选择锁定站"
+                allowClear
+                style={{ width: 192 }}
+                options={lotoOptions}
+              />
+            </div>
+            <div className="flex items-center gap-3">
+              <label className="text-sm font-medium text-gray-700 whitespace-nowrap">能量源:</label>
+              <Select
+                value={queryParams.powerType}
+                onChange={(value) => setQueryParams({ ...queryParams, powerType: value })}
+                placeholder="请选择能量源"
+                allowClear
+                style={{ width: 192 }}
+                options={powerTypeOptions}
+              />
+            </div>
+          </div>
+
+          {/* 操作按钮组 */}
+          <Space>
+            <Button
+              type="primary"
+              icon={<Search />}
+              onClick={handleQuery}
+            >
+              搜索
+            </Button>
+            <Button
+              icon={<RefreshCw />}
+              onClick={resetQuery}
+            >
+              重置
+            </Button>
+            <Button
+              type="primary"
+              icon={<Plus />}
+              onClick={() => openForm('create')}
+            >
+              新增
+            </Button>
+            <Button
+              danger
+              icon={<DeleteOutlined />}
+              disabled={selectedRowKeys.length === 0}
+              onClick={() => handleDelete()}
+            >
+              批量删除
+            </Button>
+          </Space>
+        </div>
+      </div>
+
+      {/* 表格 */}
+      <div className="bg-white rounded-lg border border-gray-200">
+        <Table
+          columns={columns}
+          dataSource={list}
+          rowKey="pointId"
+          loading={loading}
+          pagination={false}
+          scroll={{ x: 'max-content' }}
+          rowSelection={{
+            selectedRowKeys,
+            onChange: (keys) => setSelectedRowKeys(keys),
+          }}
+        />
+
+        {/* 分页 */}
+        {!loading && list.length > 0 && (
+          <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
+            <div className="flex items-center justify-between">
+              <div className="text-sm text-gray-600">
+                共 <span className="text-blue-600 font-medium">{total}</span> 条记录
+              </div>
+              <div className="flex gap-2">
+                <Button
+                  onClick={() => setQueryParams({ ...queryParams, current: (queryParams.current || 1) - 1 })}
+                  disabled={(queryParams.current || 1) <= 1}
+                >
+                  上一页
+                </Button>
+                <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
+                  {queryParams.current || 1} / {Math.ceil(total / (queryParams.size || 10)) || 1}
+                </span>
+                <Button
+                  onClick={() => setQueryParams({ ...queryParams, current: (queryParams.current || 1) + 1 })}
+                  disabled={(queryParams.current || 1) >= Math.ceil(total / (queryParams.size || 10))}
+                >
+                  下一页
+                </Button>
+              </div>
+            </div>
+          </div>
+        )}
+      </div>
+
+      {/* 表单弹窗 */}
+      <SegregationPointForm ref={formRef} onSuccess={getList} />
+    </div>
+  );
+}
+