import React, { useState, useEffect, useRef } from 'react'; import { Search, Plus, RefreshCw, Edit2, Trash2, Settings, ArrowUpDown } from 'lucide-react'; import { padLockApi, PadLockVO, PageParam } from '../api/PadLock'; import { padLockTypeApi, PadLockTypeVO, PageParam as PadLockTypePageParam } from '../api/PadLockType'; import { hardwareApi } from '../api/Hardware'; import { toast } from 'sonner'; import { Modal, Button, Input, Space, Table, Select, TreeSelect, Form, Image, Switch, Radio } from 'antd'; import { ExclamationCircleOutlined } from '@ant-design/icons'; import type { ColumnsType } from 'antd/es/table'; import { handleTree } from '../utils/tree'; import { DICT_TYPE, getStrDictOptions } from '../utils/dict'; import { dateFormatter } from '../utils/formatTime'; import UploadImg from './lockCabinet/UploadImg'; import { Button as UIButton } from './ui/button'; import { useTranslation } from 'react-i18next'; import PermissionWrapper from './PermissionWrapper'; interface PadLockManagementProps { subMenu?: string; } export default function PadLockManagement({ subMenu }: PadLockManagementProps) { const [viewMode, setViewMode] = useState<'list' | 'type'>('list'); // 'list' 挂锁列表, 'type' 挂锁类型 const [loading, setLoading] = useState(true); const [list, setList] = useState([]); const [total, setTotal] = useState(0); const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [queryParams, setQueryParams] = useState({ pageNo: 1, pageSize: 10, lockCode: undefined, lockName: undefined, enableFlag: undefined, }); const [showPadLockForm, setShowPadLockForm] = useState(false); const [editingPadLock, setEditingPadLock] = useState(null); // 挂锁类型相关状态 const [typeLoading, setTypeLoading] = useState(true); const [typeList, setTypeList] = useState([]); const [typeTreeList, setTypeTreeList] = useState([]); const [typeTotal, setTypeTotal] = useState(0); const [typeQueryParams, setTypeQueryParams] = useState({ pageNo: 1, pageSize: -1, // 使用 -1 获取全部数据用于树形结构 lockTypeCode: undefined, lockTypeName: undefined, enableFlag: undefined, }); const [showTypeForm, setShowTypeForm] = useState(false); const [editingType, setEditingType] = useState(null); const [parentTypeId, setParentTypeId] = useState(undefined); const [expandedRowKeys, setExpandedRowKeys] = useState([]); const [isExpandAll, setIsExpandAll] = useState(true); // 获取挂锁列表 const getList = async (params?: PageParam) => { const currentParams = params || queryParams; setLoading(true); try { const response = await padLockApi.listPadLock(currentParams); setList(response.list || []); setTotal(response.total || 0); } catch (error: any) { toast.error(error.message || '获取挂锁列表失败'); } finally { setLoading(false); } }; // 获取挂锁类型列表 const getTypeList = async (params?: PadLockTypePageParam) => { const currentParams = params || typeQueryParams; setTypeLoading(true); try { const response = await padLockTypeApi.listPadLockType(currentParams); const flatList = response.list || []; setTypeList(flatList); setTypeTotal(response.total || 0); // 转换为树形结构 const treeData = handleTree( flatList.map(item => ({ ...item, id: item.lockTypeId || item.id })), 'id', 'parentTypeId', 'children' ); setTypeTreeList(treeData); // 默认展开所有节点 if (isExpandAll && treeData.length > 0) { const getAllIds = (nodes: PadLockTypeVO[]): React.Key[] => { const ids: React.Key[] = []; nodes.forEach(node => { const id = node.lockTypeId || node.id; if (id) { ids.push(id); if (node.children && node.children.length > 0) { ids.push(...getAllIds(node.children)); } } }); return ids; }; setExpandedRowKeys(getAllIds(treeData)); } } catch (error: any) { toast.error(error.message || '获取挂锁类型列表失败'); } finally { setTypeLoading(false); } }; useEffect(() => { if (viewMode === 'list') { getList(); } else { getTypeList(); } }, [viewMode, queryParams.pageNo, queryParams.pageSize, typeQueryParams.pageNo, typeQueryParams.pageSize]); // 搜索挂锁 const handleQuery = () => { const newParams = { ...queryParams, pageNo: 1 }; setQueryParams(newParams); getList(newParams); }; // 重置挂锁搜索 const resetQuery = () => { const resetParams: PageParam = { pageNo: 1, pageSize: 10, lockCode: undefined, lockName: undefined, enableFlag: undefined, }; setQueryParams(resetParams); getList(resetParams); }; // 搜索挂锁类型 const handleTypeQuery = () => { const newParams = { ...typeQueryParams, pageNo: 1 }; setTypeQueryParams(newParams); getTypeList(newParams); }; // 重置挂锁类型搜索 const resetTypeQuery = () => { const resetParams: PadLockTypePageParam = { pageNo: 1, pageSize: 10, lockTypeCode: undefined, lockTypeName: undefined, enableFlag: undefined, }; setTypeQueryParams(resetParams); getTypeList(resetParams); }; // 删除挂锁 const handleDelete = async (id?: number) => { const ids = id ? [id] : selectedRowKeys.map(key => Number(key)); if (ids.length === 0) { toast.error('请选择要删除的数据'); return; } Modal.confirm({ title: '确认删除', icon: , content: (

确定要删除选中的 {ids.length} 条挂锁数据吗?

删除后无法恢复,请谨慎操作!

), okText: '确定删除', okType: 'danger', cancelText: '取消', onOk: async () => { try { await padLockApi.delPadLock(ids); toast.success('删除成功'); setSelectedRowKeys([]); await getList(); } catch (error: any) { toast.error(error.message || '删除失败'); } }, }); }; // 打开挂锁表单 const openPadLockForm = (type: 'create' | 'update', id?: number) => { if (type === 'create') { setEditingPadLock(null); setShowPadLockForm(true); } else if (type === 'update' && id) { padLockApi.getPadLockInfo(id).then((data) => { setEditingPadLock(data); setShowPadLockForm(true); }).catch((error: any) => { toast.error(error.message || '获取挂锁信息失败'); }); } }; // 保存挂锁 const savePadLock = async (formData: PadLockVO) => { try { if (editingPadLock?.lockId || editingPadLock?.id) { // 编辑模式下,合并原始数据和表单数据,确保所有必要字段都被传递 const updateData: PadLockVO = { // 保留原始数据中的字段(这些字段用户不能编辑但需要传递) ...editingPadLock, // 用表单数据覆盖可编辑字段 ...formData, // 确保 id 和 lockId 都存在 id: editingPadLock.id || editingPadLock.lockId, lockId: editingPadLock.lockId || editingPadLock.id, }; console.log('提交的挂锁数据:', updateData); // 调试用 await padLockApi.updatePadLock(updateData); toast.success('修改成功'); } else { await padLockApi.addPadLock(formData); toast.success('新增成功'); } setShowPadLockForm(false); setEditingPadLock(null); await getList(); } catch (error: any) { toast.error(error.message || '保存失败'); } }; // 删除挂锁类型 const handleDeleteType = async (id: number, name?: string) => { Modal.confirm({ title: '确认删除', icon: , content: (

确定要删除挂锁类型 "{name || '该类型'}" 吗?

删除后无法恢复,请谨慎操作!

), okText: '确定删除', okType: 'danger', cancelText: '取消', onOk: async () => { try { await padLockTypeApi.delPadLockType(id); toast.success('删除成功'); await getTypeList(); } catch (error: any) { toast.error(error.message || '删除失败'); } }, }); }; // 打开挂锁类型表单 const openTypeForm = (type?: 'create' | 'update', id?: number, parentId?: number) => { if (type === 'create') { setEditingType(null); setParentTypeId(parentId); setShowTypeForm(true); } else if (type === 'update' && id) { padLockTypeApi.getPadLockTypeInfo(id).then((data) => { setEditingType(data); setParentTypeId(undefined); setShowTypeForm(true); }).catch((error: any) => { toast.error(error.message || '获取挂锁类型信息失败'); }); } }; // 展开/折叠所有 const toggleExpandAll = () => { if (isExpandAll) { setExpandedRowKeys([]); } else { const getAllIds = (nodes: PadLockTypeVO[]): React.Key[] => { const ids: React.Key[] = []; nodes.forEach(node => { const id = node.lockTypeId || node.id; if (id) { ids.push(id); if (node.children && node.children.length > 0) { ids.push(...getAllIds(node.children)); } } }); return ids; }; setExpandedRowKeys(getAllIds(typeTreeList)); } setIsExpandAll(!isExpandAll); }; // 保存挂锁类型 const saveType = async (formData: PadLockTypeVO) => { try { if (editingType?.lockTypeId || editingType?.id) { // 编辑模式下,合并原始数据和表单数据,确保所有必要字段都被传递 const updateData: PadLockTypeVO = { // 保留原始数据中的字段(这些字段用户不能编辑但需要传递) ...editingType, // 用表单数据覆盖可编辑字段 ...formData, // 确保 id 和 lockTypeId 都存在 id: editingType.id || editingType.lockTypeId, lockTypeId: editingType.lockTypeId || editingType.id, }; console.log('提交的挂锁类型数据:', updateData); // 调试用 await padLockTypeApi.updatePadLockType(updateData); toast.success('修改成功'); } else { await padLockTypeApi.addPadLockType(formData); toast.success('新增成功'); } setShowTypeForm(false); setEditingType(null); setParentTypeId(undefined); await getTypeList(); } catch (error: any) { toast.error(error.message || '保存失败'); } }; // 挂锁列表表格列 const padLockColumns: ColumnsType = [ { title: '挂锁名称', dataIndex: 'lockName', width: 200, }, { title: '挂锁NFC', dataIndex: 'lockNfc', width: 180, ellipsis: true, }, { title: '所属硬件', dataIndex: 'hardwareName', width: 150, render: (text: string) => text || '-', }, { title: '挂锁类型', dataIndex: 'lockTypeName', width: 150, render: (text: string) => text || '-', }, { title: '挂锁型号', dataIndex: 'lockSpec', width: 150, render: (text: string) => text || '-', }, { title: '状态', dataIndex: 'exStatus', width: 100, align: 'center', render: (status: string) => { if (status === null || status === undefined) return '-'; return ( ); }, }, { title: '备注', dataIndex: 'exRemark', ellipsis: true, render: (text: string) => text || '-', }, { title: '创建时间', dataIndex: 'createTime', width: 180, render: (text: string | Date) => text ? dateFormatter(text) : '-', }, { title: '操作', width: 150, align: 'center', fixed: 'right', render: (_: any, record: PadLockVO) => (
openPadLockForm('update', record.lockId || record.id)} className="h-8 px-2" > 编辑 handleDelete(record.lockId || record.id)} className="h-8 px-2 text-red-600 hover:text-red-700" > 删除
), }, ]; // 挂锁类型表格列(树形表格) const padLockTypeColumns: ColumnsType = [ { title: '挂锁类型名称', dataIndex: 'lockTypeName', width: 200, align: 'center', }, { title: '挂锁类型图标', dataIndex: 'lockTypeIcon', width: 120, align: 'center', render: (url: string) => { if (!url) return '-'; return ( 图标 ); }, }, { title: '挂锁类型图片', dataIndex: 'lockTypeImg', width: 120, align: 'center', render: (url: string) => { if (!url) return '-'; return ( 图片 ); }, }, { title: '挂锁型号', dataIndex: 'lockTypeSpec', width: 150, align: 'center', render: (spec: string) => spec || '-', }, { title: '操作', width: 200, align: 'center', fixed: 'right', render: (_: any, record: PadLockTypeVO) => (
openTypeForm('update', record.lockTypeId || record.id)} className="h-8 px-2" > 修改 openTypeForm('create', undefined, record.lockTypeId || record.id)} className="h-8 px-2" > 新增 {record.parentTypeId !== 0 && ( handleDeleteType(record.lockTypeId || record.id!, record.lockTypeName)} className="h-8 px-2 text-red-600 hover:text-red-700" > 删除 )}
), }, ]; return (
{viewMode === 'list' ? ( <> {/* 挂锁列表 - 搜索栏和表格放在一起 */}
{/* 搜索栏 */}
{/* 搜索输入框 */}
setQueryParams({ ...queryParams, lockName: e.target.value })} onPressEnter={handleQuery} placeholder="请输入挂锁名称" className="min-w-[150px] max-w-[200px]" allowClear />
{/* 操作按钮组 */}
{/* 表格 */}
record.lockId || record.id || ''} loading={loading} pagination={false} scroll={{ x: 'max-content' }} rowSelection={{ selectedRowKeys, onChange: setSelectedRowKeys, }} /> {/* 分页 */} {!loading && list.length > 0 && (
{total} 条记录
{queryParams.pageNo} / {Math.ceil(total / queryParams.pageSize!) || 1}
)} {/* 挂锁表单弹窗 */} { setShowPadLockForm(false); setEditingPadLock(null); }} onSave={savePadLock} /> ) : ( <> {/* 挂锁类型 - 搜索栏和表格放在一起 */}
{/* 搜索栏 */}
{/* 搜索输入框 */}
setTypeQueryParams({ ...typeQueryParams, lockTypeName: e.target.value })} onPressEnter={handleTypeQuery} placeholder="请输入类型名称" className="min-w-[150px] max-w-[200px]" allowClear />
{/* 操作按钮组 */}
{/* 表格 - 树形表格 */}
record.lockTypeId || record.id || ''} loading={typeLoading} pagination={false} scroll={{ x: 'max-content' }} expandable={{ expandedRowKeys, onExpandedRowsChange: setExpandedRowKeys, defaultExpandAllRows: isExpandAll, }} /> {/* 挂锁类型表单弹窗 */} {showTypeForm && ( { setShowTypeForm(false); setEditingType(null); setParentTypeId(undefined); }} onSave={saveType} /> )} )} ); } // 挂锁类型表单弹窗组件 interface TypeFormModalProps { visible: boolean; editingType: PadLockTypeVO | null; parentTypeId?: number; typeList: PadLockTypeVO[]; onCancel: () => void; onSave: (data: PadLockTypeVO) => void; } function TypeFormModal({ visible, editingType, parentTypeId, typeList, onCancel, onSave }: TypeFormModalProps) { const { t } = useTranslation(); const [form] = Form.useForm(); const [formLoading, setFormLoading] = useState(false); const [typeTreeOptions, setTypeTreeOptions] = useState([]); // 构建树形选择器数据 useEffect(() => { if (typeList.length > 0) { const treeData = handleTree( typeList.map(item => ({ ...item, id: item.lockTypeId || item.id })), 'id', 'parentTypeId', 'children' ); const buildTreeSelectData = (nodes: PadLockTypeVO[]): any[] => { return nodes.map(node => ({ title: node.lockTypeName, value: node.lockTypeId || node.id, children: node.children ? buildTreeSelectData(node.children) : undefined, })); }; setTypeTreeOptions(buildTreeSelectData(treeData)); } }, [typeList]); useEffect(() => { if (visible) { if (editingType) { form.setFieldsValue({ lockTypeName: editingType.lockTypeName || '', parentTypeId: editingType.parentTypeId === 0 ? undefined : editingType.parentTypeId, lockTypeSpec: editingType.lockTypeSpec || '', lockTypeIcon: editingType.lockTypeIcon || '', lockTypeImg: editingType.lockTypeImg || '', }); } else { form.setFieldsValue({ lockTypeName: '', parentTypeId: parentTypeId || undefined, lockTypeSpec: '', lockTypeIcon: '', lockTypeImg: '', }); } } }, [visible, editingType, parentTypeId, form]); const handleSubmit = async () => { try { const values = await form.validateFields(); setFormLoading(true); // 编辑模式下,合并原始数据和表单数据,确保所有必要字段都被传递 const submitData: PadLockTypeVO = editingType ? { // 保留原始数据中的字段(这些字段用户不能编辑但需要传递) ...editingType, // 用表单数据覆盖可编辑字段 ...values, // 确保 parentTypeId 存在(如果表单中没有,使用原始数据或默认值 0) parentTypeId: values.parentTypeId !== undefined ? (values.parentTypeId || 0) : (editingType.parentTypeId || 0), // 确保 id 和 lockTypeId 都存在 id: editingType.id || editingType.lockTypeId, lockTypeId: editingType.lockTypeId || editingType.id, } : { // 新增模式下,只传递表单数据 ...values, parentTypeId: values.parentTypeId || 0, }; onSave(submitData); } catch (error) { // 表单验证失败 } finally { setFormLoading(false); } }; return (
{editingType?.parentTypeId !== 0 && ( )} form.setFieldsValue({ lockTypeIcon: value })} limit={1} height="100px" width="100px" /> form.setFieldsValue({ lockTypeImg: value })} limit={1} height="100px" width="100px" />
); } // 挂锁表单弹窗组件 interface PadLockFormModalProps { visible: boolean; editingPadLock: PadLockVO | null; onCancel: () => void; onSave: (data: PadLockVO) => void; } function PadLockFormModal({ visible, editingPadLock, onCancel, onSave }: PadLockFormModalProps) { const { t } = useTranslation(); const [form] = Form.useForm(); const [formLoading, setFormLoading] = useState(false); const [hardwareOptions, setHardwareOptions] = useState<{ label: string; value: number }[]>([]); const [lockTypeTreeOptions, setLockTypeTreeOptions] = useState([]); const [statusOptions] = useState(() => { // 使用默认状态选项 return [ { dictType: 'common_status', label: '启用', value: '1' }, { dictType: 'common_status', label: '禁用', value: '0' }, ]; }); // 获取硬件列表 const getHardwareList = async () => { try { const response = await hardwareApi.listHardware({ pageNo: 1, pageSize: -1 }); const options = response.list.map(item => ({ label: item.hardwareName, value: item.id!, })); setHardwareOptions(options); } catch (error) { console.error('获取硬件列表失败:', error); } }; // 获取挂锁类型列表 const getLockTypeList = async () => { try { const response = await padLockTypeApi.listPadLockType({ pageNo: 1, pageSize: -1 }); const flatList = response.list || []; // 转换为树形结构 const treeData = handleTree( flatList.map(item => ({ ...item, id: item.lockTypeId || item.id })), 'id', 'parentTypeId', 'children' ); const buildTreeSelectData = (nodes: PadLockTypeVO[]): any[] => { return nodes.map(node => ({ title: node.lockTypeName, value: node.lockTypeId || node.id, children: node.children ? buildTreeSelectData(node.children) : undefined, })); }; setLockTypeTreeOptions(buildTreeSelectData(treeData)); } catch (error) { console.error('获取挂锁类型列表失败:', error); } }; useEffect(() => { if (visible) { getHardwareList(); getLockTypeList(); if (editingPadLock) { form.setFieldsValue({ hardwareId: editingPadLock.hardwareId, lockTypeId: editingPadLock.lockTypeId, lockName: editingPadLock.lockName || '', lockNfc: editingPadLock.lockNfc || '', lockSpec: editingPadLock.lockSpec || '', exStatus: editingPadLock.exStatus || '1', exRemark: editingPadLock.exRemark || '', }); } else { form.setFieldsValue({ hardwareId: undefined, lockTypeId: undefined, lockName: '', lockNfc: '', lockSpec: '', exStatus: '1', exRemark: '', }); } } }, [visible, editingPadLock, form]); const handleSubmit = async () => { try { const values = await form.validateFields(); setFormLoading(true); // 编辑模式下,合并原始数据和表单数据,确保所有必要字段都被传递 const submitData: PadLockVO = editingPadLock ? { // 保留原始数据中的字段(这些字段用户不能编辑但需要传递) ...editingPadLock, // 用表单数据覆盖可编辑字段 ...values, // 确保 id 和 lockId 都存在 id: editingPadLock.id || editingPadLock.lockId, lockId: editingPadLock.lockId || editingPadLock.id, } : { // 新增模式下,只传递表单数据 ...values, }; onSave(submitData); } catch (error) { // 表单验证失败 } finally { setFormLoading(false); } }; return (
{statusOptions.map(option => ( {option.label} ))}
); }