|
|
@@ -1,11 +1,17 @@
|
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
|
-import { Search, Plus, RefreshCw, Edit2, Trash2, Settings } from 'lucide-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 } from 'antd';
|
|
|
+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';
|
|
|
|
|
|
interface PadLockManagementProps {
|
|
|
subMenu?: string;
|
|
|
@@ -16,6 +22,7 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
const [list, setList] = useState<PadLockVO[]>([]);
|
|
|
const [total, setTotal] = useState(0);
|
|
|
+ const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
|
|
const [queryParams, setQueryParams] = useState<PageParam>({
|
|
|
pageNo: 1,
|
|
|
pageSize: 10,
|
|
|
@@ -23,20 +30,26 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
lockName: undefined,
|
|
|
enableFlag: undefined,
|
|
|
});
|
|
|
+ const [showPadLockForm, setShowPadLockForm] = useState(false);
|
|
|
+ const [editingPadLock, setEditingPadLock] = useState<PadLockVO | null>(null);
|
|
|
|
|
|
// 挂锁类型相关状态
|
|
|
const [typeLoading, setTypeLoading] = useState(true);
|
|
|
const [typeList, setTypeList] = useState<PadLockTypeVO[]>([]);
|
|
|
+ const [typeTreeList, setTypeTreeList] = useState<PadLockTypeVO[]>([]);
|
|
|
const [typeTotal, setTypeTotal] = useState(0);
|
|
|
const [typeQueryParams, setTypeQueryParams] = useState<PadLockTypePageParam>({
|
|
|
pageNo: 1,
|
|
|
- pageSize: 10,
|
|
|
+ pageSize: -1, // 使用 -1 获取全部数据用于树形结构
|
|
|
lockTypeCode: undefined,
|
|
|
lockTypeName: undefined,
|
|
|
enableFlag: undefined,
|
|
|
});
|
|
|
const [showTypeForm, setShowTypeForm] = useState(false);
|
|
|
const [editingType, setEditingType] = useState<PadLockTypeVO | null>(null);
|
|
|
+ const [parentTypeId, setParentTypeId] = useState<number | undefined>(undefined);
|
|
|
+ const [expandedRowKeys, setExpandedRowKeys] = useState<React.Key[]>([]);
|
|
|
+ const [isExpandAll, setIsExpandAll] = useState(true);
|
|
|
|
|
|
// 获取挂锁列表
|
|
|
const getList = async (params?: PageParam) => {
|
|
|
@@ -59,8 +72,36 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
setTypeLoading(true);
|
|
|
try {
|
|
|
const response = await padLockTypeApi.listPadLockType(currentParams);
|
|
|
- setTypeList(response.list || []);
|
|
|
+ const flatList = response.list || [];
|
|
|
+ setTypeList(flatList);
|
|
|
setTypeTotal(response.total || 0);
|
|
|
+
|
|
|
+ // 转换为树形结构
|
|
|
+ const treeData = handleTree<PadLockTypeVO>(
|
|
|
+ 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 {
|
|
|
@@ -117,13 +158,19 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
};
|
|
|
|
|
|
// 删除挂锁
|
|
|
- const handleDelete = async (id: number, name?: string) => {
|
|
|
+ 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: <ExclamationCircleOutlined />,
|
|
|
content: (
|
|
|
<div>
|
|
|
- <p>确定要删除挂锁 <strong>"{name || '该挂锁'}"</strong> 吗?</p>
|
|
|
+ <p>确定要删除选中的 {ids.length} 条挂锁数据吗?</p>
|
|
|
<p style={{ color: '#ff4d4f', marginTop: '8px' }}>删除后无法恢复,请谨慎操作!</p>
|
|
|
</div>
|
|
|
),
|
|
|
@@ -132,8 +179,9 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
cancelText: '取消',
|
|
|
onOk: async () => {
|
|
|
try {
|
|
|
- await padLockApi.delPadLock(id);
|
|
|
+ await padLockApi.delPadLock(ids);
|
|
|
toast.success('删除成功');
|
|
|
+ setSelectedRowKeys([]);
|
|
|
await getList();
|
|
|
} catch (error: any) {
|
|
|
toast.error(error.message || '删除失败');
|
|
|
@@ -142,6 +190,42 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
+ // 打开挂锁表单
|
|
|
+ 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) {
|
|
|
+ await padLockApi.updatePadLock({
|
|
|
+ ...formData,
|
|
|
+ lockId: editingPadLock.lockId || editingPadLock.id,
|
|
|
+ });
|
|
|
+ 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({
|
|
|
@@ -169,13 +253,15 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
};
|
|
|
|
|
|
// 打开挂锁类型表单
|
|
|
- const openTypeForm = (type?: 'create' | 'update', id?: number) => {
|
|
|
+ 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 || '获取挂锁类型信息失败');
|
|
|
@@ -183,13 +269,36 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ // 展开/折叠所有
|
|
|
+ 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) {
|
|
|
+ if (editingType?.lockTypeId || editingType?.id) {
|
|
|
await padLockTypeApi.updatePadLockType({
|
|
|
...formData,
|
|
|
- lockTypeId: editingType.lockTypeId,
|
|
|
+ lockTypeId: editingType.lockTypeId || editingType.id,
|
|
|
});
|
|
|
toast.success('修改成功');
|
|
|
} else {
|
|
|
@@ -198,6 +307,7 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
}
|
|
|
setShowTypeForm(false);
|
|
|
setEditingType(null);
|
|
|
+ setParentTypeId(undefined);
|
|
|
await getTypeList();
|
|
|
} catch (error: any) {
|
|
|
toast.error(error.message || '保存失败');
|
|
|
@@ -207,139 +317,187 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
// 挂锁列表表格列
|
|
|
const padLockColumns: ColumnsType<PadLockVO> = [
|
|
|
{
|
|
|
- title: '序号',
|
|
|
- width: 80,
|
|
|
- align: 'center',
|
|
|
- render: (_: any, __: PadLockVO, index: number) => {
|
|
|
- return (queryParams.pageNo - 1) * queryParams.pageSize + index + 1;
|
|
|
- },
|
|
|
+ title: '挂锁名称',
|
|
|
+ dataIndex: 'lockName',
|
|
|
+ width: 200,
|
|
|
},
|
|
|
{
|
|
|
- title: '挂锁编号',
|
|
|
- dataIndex: 'lockId',
|
|
|
- width: 100,
|
|
|
- align: 'center',
|
|
|
+ title: '挂锁NFC',
|
|
|
+ dataIndex: 'lockNfc',
|
|
|
+ width: 180,
|
|
|
+ ellipsis: true,
|
|
|
},
|
|
|
{
|
|
|
- title: '挂锁编码',
|
|
|
- dataIndex: 'lockCode',
|
|
|
+ title: '所属硬件',
|
|
|
+ dataIndex: 'hardwareName',
|
|
|
width: 150,
|
|
|
+ render: (text: string) => text || '-',
|
|
|
},
|
|
|
{
|
|
|
- title: '挂锁名称',
|
|
|
- dataIndex: 'lockName',
|
|
|
- width: 200,
|
|
|
+ title: '挂锁类型',
|
|
|
+ dataIndex: 'lockTypeName',
|
|
|
+ width: 150,
|
|
|
+ render: (text: string) => text || '-',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '挂锁型号',
|
|
|
+ dataIndex: 'lockSpec',
|
|
|
+ width: 150,
|
|
|
+ render: (text: string) => text || '-',
|
|
|
},
|
|
|
{
|
|
|
title: '状态',
|
|
|
- dataIndex: 'enableFlag',
|
|
|
+ dataIndex: 'exStatus',
|
|
|
width: 100,
|
|
|
align: 'center',
|
|
|
- render: (flag: string) => {
|
|
|
- return flag === '1' || flag === 'Y' ? '启用' : '禁用';
|
|
|
+ render: (status: string) => {
|
|
|
+ if (status === null || status === undefined) return '-';
|
|
|
+ return (
|
|
|
+ <Switch
|
|
|
+ checked={status === '1'}
|
|
|
+ disabled
|
|
|
+ checkedChildren="启用"
|
|
|
+ unCheckedChildren="禁用"
|
|
|
+ />
|
|
|
+ );
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
title: '备注',
|
|
|
- dataIndex: 'remark',
|
|
|
+ dataIndex: 'exRemark',
|
|
|
ellipsis: true,
|
|
|
+ render: (text: string) => text || '-',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '创建时间',
|
|
|
+ dataIndex: 'createTime',
|
|
|
+ width: 180,
|
|
|
+ render: (text: string | Date) => text ? dateFormatter(text) : '-',
|
|
|
},
|
|
|
{
|
|
|
title: '操作',
|
|
|
- width: 120,
|
|
|
+ width: 150,
|
|
|
align: 'center',
|
|
|
fixed: 'right',
|
|
|
render: (_: any, record: PadLockVO) => (
|
|
|
- <Space>
|
|
|
- <Button
|
|
|
- type="link"
|
|
|
- icon={<Edit2 className="w-4 h-4" />}
|
|
|
- onClick={() => {
|
|
|
- // TODO: 实现编辑功能
|
|
|
- toast.info('编辑功能待实现');
|
|
|
- }}
|
|
|
- title="编辑"
|
|
|
- />
|
|
|
- <Button
|
|
|
- type="link"
|
|
|
- danger
|
|
|
- icon={<Trash2 className="w-4 h-4" />}
|
|
|
- onClick={() => handleDelete(record.lockId!, record.lockName)}
|
|
|
- title="删除"
|
|
|
- />
|
|
|
- </Space>
|
|
|
+ <div className="flex items-center gap-2 justify-center">
|
|
|
+ <UIButton
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => openPadLockForm('update', record.lockId || record.id)}
|
|
|
+ className="h-8 px-2"
|
|
|
+ >
|
|
|
+ <Edit2 className="w-4 h-4" />
|
|
|
+ <span className="ml-1">编辑</span>
|
|
|
+ </UIButton>
|
|
|
+ <UIButton
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => handleDelete(record.lockId || record.id)}
|
|
|
+ className="h-8 px-2 text-red-600 hover:text-red-700"
|
|
|
+ >
|
|
|
+ <Trash2 className="w-4 h-4" />
|
|
|
+ <span className="ml-1">删除</span>
|
|
|
+ </UIButton>
|
|
|
+ </div>
|
|
|
),
|
|
|
},
|
|
|
];
|
|
|
|
|
|
- // 挂锁类型表格列
|
|
|
+ // 挂锁类型表格列(树形表格)
|
|
|
const padLockTypeColumns: ColumnsType<PadLockTypeVO> = [
|
|
|
{
|
|
|
- title: '序号',
|
|
|
- width: 80,
|
|
|
- align: 'center',
|
|
|
- render: (_: any, __: PadLockTypeVO, index: number) => {
|
|
|
- return (typeQueryParams.pageNo - 1) * typeQueryParams.pageSize + index + 1;
|
|
|
- },
|
|
|
- },
|
|
|
- {
|
|
|
- title: '类型编号',
|
|
|
- dataIndex: 'lockTypeId',
|
|
|
- width: 100,
|
|
|
- align: 'center',
|
|
|
- },
|
|
|
- {
|
|
|
- title: '类型编码',
|
|
|
- dataIndex: 'lockTypeCode',
|
|
|
- width: 150,
|
|
|
- },
|
|
|
- {
|
|
|
- title: '类型名称',
|
|
|
+ title: '挂锁类型名称',
|
|
|
dataIndex: 'lockTypeName',
|
|
|
width: 200,
|
|
|
+ align: 'center',
|
|
|
},
|
|
|
{
|
|
|
- title: '父类型',
|
|
|
- dataIndex: 'parentTypeId',
|
|
|
- width: 100,
|
|
|
+ title: '挂锁类型图标',
|
|
|
+ dataIndex: 'lockTypeIcon',
|
|
|
+ width: 120,
|
|
|
align: 'center',
|
|
|
- render: (id: number) => id || '-',
|
|
|
+ render: (url: string) => {
|
|
|
+ if (!url) return '-';
|
|
|
+ return (
|
|
|
+ <Image
|
|
|
+ src={url}
|
|
|
+ alt="图标"
|
|
|
+ width={50}
|
|
|
+ height={50}
|
|
|
+ style={{ objectFit: 'cover' }}
|
|
|
+ preview={{
|
|
|
+ mask: '查看',
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ },
|
|
|
},
|
|
|
{
|
|
|
- title: '状态',
|
|
|
- dataIndex: 'enableFlag',
|
|
|
- width: 100,
|
|
|
+ title: '挂锁类型图片',
|
|
|
+ dataIndex: 'lockTypeImg',
|
|
|
+ width: 120,
|
|
|
align: 'center',
|
|
|
- render: (flag: string) => {
|
|
|
- return flag === '1' || flag === 'Y' ? '启用' : '禁用';
|
|
|
+ render: (url: string) => {
|
|
|
+ if (!url) return '-';
|
|
|
+ return (
|
|
|
+ <Image
|
|
|
+ src={url}
|
|
|
+ alt="图片"
|
|
|
+ width={50}
|
|
|
+ height={50}
|
|
|
+ style={{ objectFit: 'cover' }}
|
|
|
+ preview={{
|
|
|
+ mask: '查看',
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ );
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
- title: '备注',
|
|
|
- dataIndex: 'remark',
|
|
|
- ellipsis: true,
|
|
|
+ title: '挂锁型号',
|
|
|
+ dataIndex: 'lockTypeSpec',
|
|
|
+ width: 150,
|
|
|
+ align: 'center',
|
|
|
+ render: (spec: string) => spec || '-',
|
|
|
},
|
|
|
{
|
|
|
title: '操作',
|
|
|
- width: 120,
|
|
|
+ width: 200,
|
|
|
align: 'center',
|
|
|
fixed: 'right',
|
|
|
render: (_: any, record: PadLockTypeVO) => (
|
|
|
- <Space>
|
|
|
- <Button
|
|
|
- type="link"
|
|
|
- icon={<Edit2 className="w-4 h-4" />}
|
|
|
- onClick={() => openTypeForm('update', record.lockTypeId)}
|
|
|
- title="编辑"
|
|
|
- />
|
|
|
- <Button
|
|
|
- type="link"
|
|
|
- danger
|
|
|
- icon={<Trash2 className="w-4 h-4" />}
|
|
|
- onClick={() => handleDeleteType(record.lockTypeId!, record.lockTypeName)}
|
|
|
- title="删除"
|
|
|
- />
|
|
|
- </Space>
|
|
|
+ <div className="flex items-center gap-2 justify-center">
|
|
|
+ <UIButton
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => openTypeForm('update', record.lockTypeId || record.id)}
|
|
|
+ className="h-8 px-2"
|
|
|
+ >
|
|
|
+ <Edit2 className="w-4 h-4" />
|
|
|
+ <span className="ml-1">修改</span>
|
|
|
+ </UIButton>
|
|
|
+ <UIButton
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => openTypeForm('create', undefined, record.lockTypeId || record.id)}
|
|
|
+ className="h-8 px-2"
|
|
|
+ >
|
|
|
+ <Plus className="w-4 h-4" />
|
|
|
+ <span className="ml-1">新增</span>
|
|
|
+ </UIButton>
|
|
|
+ {record.parentTypeId !== 0 && (
|
|
|
+ <UIButton
|
|
|
+ variant="ghost"
|
|
|
+ size="sm"
|
|
|
+ onClick={() => handleDeleteType(record.lockTypeId || record.id!, record.lockTypeName)}
|
|
|
+ className="h-8 px-2 text-red-600 hover:text-red-700"
|
|
|
+ >
|
|
|
+ <Trash2 className="w-4 h-4" />
|
|
|
+ <span className="ml-1">删除</span>
|
|
|
+ </UIButton>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
),
|
|
|
},
|
|
|
];
|
|
|
@@ -348,22 +506,13 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
<div className="space-y-4">
|
|
|
{viewMode === 'list' ? (
|
|
|
<>
|
|
|
- {/* 挂锁列表搜索栏 */}
|
|
|
- <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="bg-white rounded-xl border border-gray-200/50 shadow-sm overflow-hidden">
|
|
|
+ {/* 搜索栏 */}
|
|
|
+ <div className="p-5 border-b border-gray-200">
|
|
|
+ <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.lockCode || ''}
|
|
|
- onChange={(e) => setQueryParams({ ...queryParams, lockCode: 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>
|
|
|
<Input
|
|
|
@@ -377,32 +526,38 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- {/* 操作按钮组 */}
|
|
|
- <Space>
|
|
|
+ {/* 操作按钮组 */}
|
|
|
+ <Space>
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ icon={<Search className="w-4 h-4" />}
|
|
|
+ onClick={handleQuery}
|
|
|
+ >
|
|
|
+ 搜索
|
|
|
+ </Button>
|
|
|
+
|
|
|
+ <Button
|
|
|
+ icon={<RefreshCw className="w-4 h-4" />}
|
|
|
+ onClick={resetQuery}
|
|
|
+ >
|
|
|
+ 重置
|
|
|
+ </Button>
|
|
|
+
|
|
|
<Button
|
|
|
type="primary"
|
|
|
- icon={<Search className="w-4 h-4" />}
|
|
|
- onClick={handleQuery}
|
|
|
+ icon={<Plus className="w-4 h-4" />}
|
|
|
+ onClick={() => openPadLockForm('create')}
|
|
|
>
|
|
|
- 搜索
|
|
|
+ 新增
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
- icon={<RefreshCw className="w-4 h-4" />}
|
|
|
- onClick={resetQuery}
|
|
|
+ danger
|
|
|
+ icon={<Trash2 className="w-4 h-4" />}
|
|
|
+ onClick={() => handleDelete()}
|
|
|
+ disabled={selectedRowKeys.length === 0}
|
|
|
>
|
|
|
- 重置
|
|
|
- </Button>
|
|
|
-
|
|
|
- <Button
|
|
|
- type="primary"
|
|
|
- icon={<Plus className="w-4 h-4" />}
|
|
|
- onClick={() => {
|
|
|
- // TODO: 实现新增挂锁功能
|
|
|
- toast.info('新增挂锁功能待实现');
|
|
|
- }}
|
|
|
- >
|
|
|
- 新增
|
|
|
+ 批量删除
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
@@ -411,20 +566,25 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
>
|
|
|
设置挂锁类型
|
|
|
</Button>
|
|
|
- </Space>
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- {/* 挂锁列表表格 */}
|
|
|
- <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm overflow-hidden">
|
|
|
- <Table
|
|
|
- columns={padLockColumns}
|
|
|
- dataSource={list}
|
|
|
- rowKey="lockId"
|
|
|
- loading={loading}
|
|
|
- pagination={false}
|
|
|
- scroll={{ x: 'max-content' }}
|
|
|
- />
|
|
|
+ {/* 表格 */}
|
|
|
+ <div>
|
|
|
+ <Table
|
|
|
+ columns={padLockColumns}
|
|
|
+ dataSource={list}
|
|
|
+ rowKey={(record) => record.lockId || record.id || ''}
|
|
|
+ loading={loading}
|
|
|
+ pagination={false}
|
|
|
+ scroll={{ x: 'max-content' }}
|
|
|
+ rowSelection={{
|
|
|
+ selectedRowKeys,
|
|
|
+ onChange: setSelectedRowKeys,
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
{/* 分页 */}
|
|
|
@@ -457,117 +617,97 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
</>
|
|
|
) : (
|
|
|
<>
|
|
|
- {/* 挂锁类型搜索栏 */}
|
|
|
- <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={typeQueryParams.lockTypeCode || ''}
|
|
|
- onChange={(e) => setTypeQueryParams({ ...typeQueryParams, lockTypeCode: e.target.value })}
|
|
|
- onPressEnter={handleTypeQuery}
|
|
|
- placeholder="请输入类型编码"
|
|
|
- style={{ width: 192 }}
|
|
|
- allowClear
|
|
|
- />
|
|
|
+ {/* 挂锁类型 - 搜索栏和表格放在一起 */}
|
|
|
+ <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm overflow-hidden">
|
|
|
+ {/* 搜索栏 */}
|
|
|
+ <div className="p-5 border-b border-gray-200">
|
|
|
+ <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={typeQueryParams.lockTypeName || ''}
|
|
|
+ onChange={(e) => setTypeQueryParams({ ...typeQueryParams, lockTypeName: e.target.value })}
|
|
|
+ onPressEnter={handleTypeQuery}
|
|
|
+ placeholder="请输入类型名称"
|
|
|
+ style={{ width: 192 }}
|
|
|
+ allowClear
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div className="flex items-center gap-3">
|
|
|
- <label className="text-sm font-medium text-gray-700 whitespace-nowrap">类型名称:</label>
|
|
|
- <Input
|
|
|
- value={typeQueryParams.lockTypeName || ''}
|
|
|
- onChange={(e) => setTypeQueryParams({ ...typeQueryParams, lockTypeName: e.target.value })}
|
|
|
- onPressEnter={handleTypeQuery}
|
|
|
- placeholder="请输入类型名称"
|
|
|
- style={{ width: 192 }}
|
|
|
- allowClear
|
|
|
- />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- {/* 操作按钮组 */}
|
|
|
- <Space>
|
|
|
- <Button
|
|
|
- type="primary"
|
|
|
- icon={<Search className="w-4 h-4" />}
|
|
|
- onClick={handleTypeQuery}
|
|
|
- >
|
|
|
- 搜索
|
|
|
- </Button>
|
|
|
-
|
|
|
- <Button
|
|
|
- icon={<RefreshCw className="w-4 h-4" />}
|
|
|
- onClick={resetTypeQuery}
|
|
|
- >
|
|
|
- 重置
|
|
|
- </Button>
|
|
|
-
|
|
|
- <Button
|
|
|
- type="primary"
|
|
|
- icon={<Plus className="w-4 h-4" />}
|
|
|
- onClick={() => openTypeForm('create')}
|
|
|
- >
|
|
|
- 新增
|
|
|
- </Button>
|
|
|
-
|
|
|
- <Button
|
|
|
- onClick={() => setViewMode('list')}
|
|
|
- >
|
|
|
- 返回挂锁列表
|
|
|
- </Button>
|
|
|
- </Space>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* 挂锁类型表格 */}
|
|
|
- <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm overflow-hidden">
|
|
|
- <Table
|
|
|
- columns={padLockTypeColumns}
|
|
|
- dataSource={typeList}
|
|
|
- rowKey="lockTypeId"
|
|
|
- loading={typeLoading}
|
|
|
- pagination={false}
|
|
|
- scroll={{ x: 'max-content' }}
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* 分页 */}
|
|
|
- {!typeLoading && typeList.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">{typeTotal}</span> 条记录
|
|
|
- </div>
|
|
|
- <div className="flex gap-2">
|
|
|
+ {/* 操作按钮组 */}
|
|
|
+ <Space>
|
|
|
<Button
|
|
|
- onClick={() => setTypeQueryParams({ ...typeQueryParams, pageNo: typeQueryParams.pageNo! - 1 })}
|
|
|
- disabled={typeQueryParams.pageNo! <= 1}
|
|
|
+ type="primary"
|
|
|
+ icon={<Search className="w-4 h-4" />}
|
|
|
+ onClick={handleTypeQuery}
|
|
|
>
|
|
|
- 上一页
|
|
|
+ 搜索
|
|
|
</Button>
|
|
|
- <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
|
|
|
- {typeQueryParams.pageNo} / {Math.ceil(typeTotal / typeQueryParams.pageSize!) || 1}
|
|
|
- </span>
|
|
|
+
|
|
|
<Button
|
|
|
- onClick={() => setTypeQueryParams({ ...typeQueryParams, pageNo: typeQueryParams.pageNo! + 1 })}
|
|
|
- disabled={typeQueryParams.pageNo! >= Math.ceil(typeTotal / typeQueryParams.pageSize!)}
|
|
|
+ icon={<RefreshCw className="w-4 h-4" />}
|
|
|
+ onClick={resetTypeQuery}
|
|
|
>
|
|
|
- 下一页
|
|
|
+ 重置
|
|
|
</Button>
|
|
|
- </div>
|
|
|
+
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ icon={<Plus className="w-4 h-4" />}
|
|
|
+ onClick={() => openTypeForm('create')}
|
|
|
+ >
|
|
|
+ 新增
|
|
|
+ </Button>
|
|
|
+
|
|
|
+ <Button
|
|
|
+ icon={<ArrowUpDown className="w-4 h-4" />}
|
|
|
+ onClick={toggleExpandAll}
|
|
|
+ >
|
|
|
+ 展开/折叠
|
|
|
+ </Button>
|
|
|
+
|
|
|
+ <Button
|
|
|
+ onClick={() => setViewMode('list')}
|
|
|
+ >
|
|
|
+ 返回挂锁列表
|
|
|
+ </Button>
|
|
|
+ </Space>
|
|
|
</div>
|
|
|
</div>
|
|
|
- )}
|
|
|
+
|
|
|
+ {/* 表格 - 树形表格 */}
|
|
|
+ <div>
|
|
|
+ <Table
|
|
|
+ columns={padLockTypeColumns}
|
|
|
+ dataSource={typeTreeList}
|
|
|
+ rowKey={(record) => record.lockTypeId || record.id || ''}
|
|
|
+ loading={typeLoading}
|
|
|
+ pagination={false}
|
|
|
+ scroll={{ x: 'max-content' }}
|
|
|
+ expandable={{
|
|
|
+ expandedRowKeys,
|
|
|
+ onExpandedRowsChange: setExpandedRowKeys,
|
|
|
+ defaultExpandAllRows: isExpandAll,
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
|
|
|
{/* 挂锁类型表单弹窗 */}
|
|
|
{showTypeForm && (
|
|
|
<TypeFormModal
|
|
|
visible={showTypeForm}
|
|
|
editingType={editingType}
|
|
|
+ parentTypeId={parentTypeId}
|
|
|
+ typeList={typeList}
|
|
|
onCancel={() => {
|
|
|
setShowTypeForm(false);
|
|
|
setEditingType(null);
|
|
|
+ setParentTypeId(undefined);
|
|
|
}}
|
|
|
onSave={saveType}
|
|
|
/>
|
|
|
@@ -582,49 +722,78 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
|
|
|
interface TypeFormModalProps {
|
|
|
visible: boolean;
|
|
|
editingType: PadLockTypeVO | null;
|
|
|
+ parentTypeId?: number;
|
|
|
+ typeList: PadLockTypeVO[];
|
|
|
onCancel: () => void;
|
|
|
onSave: (data: PadLockTypeVO) => void;
|
|
|
}
|
|
|
|
|
|
-function TypeFormModal({ visible, editingType, onCancel, onSave }: TypeFormModalProps) {
|
|
|
- const [formData, setFormData] = useState<PadLockTypeVO>({
|
|
|
- lockTypeCode: '',
|
|
|
- lockTypeName: '',
|
|
|
- parentTypeId: undefined,
|
|
|
- enableFlag: '1',
|
|
|
- remark: '',
|
|
|
- });
|
|
|
+function TypeFormModal({ visible, editingType, parentTypeId, typeList, onCancel, onSave }: TypeFormModalProps) {
|
|
|
+ const [form] = Form.useForm();
|
|
|
+ const [formLoading, setFormLoading] = useState(false);
|
|
|
+ const [typeTreeOptions, setTypeTreeOptions] = useState<any[]>([]);
|
|
|
|
|
|
+ // 构建树形选择器数据
|
|
|
useEffect(() => {
|
|
|
- if (editingType) {
|
|
|
- setFormData({
|
|
|
- lockTypeCode: editingType.lockTypeCode || '',
|
|
|
- lockTypeName: editingType.lockTypeName || '',
|
|
|
- parentTypeId: editingType.parentTypeId,
|
|
|
- enableFlag: editingType.enableFlag || '1',
|
|
|
- remark: editingType.remark || '',
|
|
|
- });
|
|
|
- } else {
|
|
|
- setFormData({
|
|
|
- lockTypeCode: '',
|
|
|
- lockTypeName: '',
|
|
|
- parentTypeId: undefined,
|
|
|
- enableFlag: '1',
|
|
|
- remark: '',
|
|
|
- });
|
|
|
+ if (typeList.length > 0) {
|
|
|
+ const treeData = handleTree<PadLockTypeVO>(
|
|
|
+ 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));
|
|
|
}
|
|
|
- }, [editingType, visible]);
|
|
|
+ }, [typeList]);
|
|
|
|
|
|
- const handleSubmit = () => {
|
|
|
- if (!formData.lockTypeCode.trim()) {
|
|
|
- toast.error('请输入类型编码');
|
|
|
- return;
|
|
|
+ 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: '',
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
- if (!formData.lockTypeName.trim()) {
|
|
|
- toast.error('请输入类型名称');
|
|
|
- return;
|
|
|
+ }, [visible, editingType, parentTypeId, form]);
|
|
|
+
|
|
|
+ const handleSubmit = async () => {
|
|
|
+ try {
|
|
|
+ const values = await form.validateFields();
|
|
|
+ setFormLoading(true);
|
|
|
+
|
|
|
+ const submitData: PadLockTypeVO = {
|
|
|
+ ...values,
|
|
|
+ parentTypeId: values.parentTypeId || 0,
|
|
|
+ lockTypeId: editingType?.lockTypeId || editingType?.id,
|
|
|
+ };
|
|
|
+
|
|
|
+ onSave(submitData);
|
|
|
+ } catch (error) {
|
|
|
+ // 表单验证失败
|
|
|
+ } finally {
|
|
|
+ setFormLoading(false);
|
|
|
}
|
|
|
- onSave(formData);
|
|
|
};
|
|
|
|
|
|
return (
|
|
|
@@ -635,58 +804,265 @@ function TypeFormModal({ visible, editingType, onCancel, onSave }: TypeFormModal
|
|
|
onCancel={onCancel}
|
|
|
okText="确定"
|
|
|
cancelText="取消"
|
|
|
+ confirmLoading={formLoading}
|
|
|
width={600}
|
|
|
>
|
|
|
- <div className="space-y-4 mt-4">
|
|
|
- <div>
|
|
|
- <label className="block text-sm text-gray-700 mb-2">类型编码 *</label>
|
|
|
- <Input
|
|
|
- value={formData.lockTypeCode}
|
|
|
- onChange={(e) => setFormData({ ...formData, lockTypeCode: e.target.value })}
|
|
|
- placeholder="请输入类型编码"
|
|
|
- />
|
|
|
- </div>
|
|
|
- <div>
|
|
|
- <label className="block text-sm text-gray-700 mb-2">类型名称 *</label>
|
|
|
- <Input
|
|
|
- value={formData.lockTypeName}
|
|
|
- onChange={(e) => setFormData({ ...formData, lockTypeName: e.target.value })}
|
|
|
- placeholder="请输入类型名称"
|
|
|
+ <Form
|
|
|
+ form={form}
|
|
|
+ layout="vertical"
|
|
|
+ className="mt-4"
|
|
|
+ >
|
|
|
+ {editingType?.parentTypeId !== 0 && (
|
|
|
+ <Form.Item
|
|
|
+ label="父类型"
|
|
|
+ name="parentTypeId"
|
|
|
+ >
|
|
|
+ <TreeSelect
|
|
|
+ treeData={typeTreeOptions}
|
|
|
+ placeholder="选择父级"
|
|
|
+ allowClear
|
|
|
+ treeDefaultExpandAll
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+ )}
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="挂锁类型名称"
|
|
|
+ name="lockTypeName"
|
|
|
+ rules={[{ required: true, message: '请输入挂锁类型名称' }]}
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入挂锁类型名称" />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="挂锁型号"
|
|
|
+ name="lockTypeSpec"
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入挂锁型号" />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="挂锁类型图标"
|
|
|
+ name="lockTypeIcon"
|
|
|
+ >
|
|
|
+ <UploadImg
|
|
|
+ value={form.getFieldValue('lockTypeIcon')}
|
|
|
+ onChange={(value) => form.setFieldsValue({ lockTypeIcon: value })}
|
|
|
+ limit={1}
|
|
|
+ height="100px"
|
|
|
+ width="100px"
|
|
|
/>
|
|
|
- </div>
|
|
|
- <div>
|
|
|
- <label className="block text-sm text-gray-700 mb-2">父类型ID</label>
|
|
|
- <Input
|
|
|
- type="number"
|
|
|
- value={formData.parentTypeId || ''}
|
|
|
- onChange={(e) => setFormData({
|
|
|
- ...formData,
|
|
|
- parentTypeId: e.target.value ? Number(e.target.value) : undefined
|
|
|
- })}
|
|
|
- placeholder="请输入父类型ID(可选)"
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="挂锁类型图片"
|
|
|
+ name="lockTypeImg"
|
|
|
+ >
|
|
|
+ <UploadImg
|
|
|
+ value={form.getFieldValue('lockTypeImg')}
|
|
|
+ onChange={(value) => form.setFieldsValue({ lockTypeImg: value })}
|
|
|
+ limit={1}
|
|
|
+ height="100px"
|
|
|
+ width="100px"
|
|
|
/>
|
|
|
- </div>
|
|
|
- <div>
|
|
|
- <label className="block text-sm text-gray-700 mb-2">状态</label>
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Modal>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+// 挂锁表单弹窗组件
|
|
|
+interface PadLockFormModalProps {
|
|
|
+ visible: boolean;
|
|
|
+ editingPadLock: PadLockVO | null;
|
|
|
+ onCancel: () => void;
|
|
|
+ onSave: (data: PadLockVO) => void;
|
|
|
+}
|
|
|
+
|
|
|
+function PadLockFormModal({ visible, editingPadLock, onCancel, onSave }: PadLockFormModalProps) {
|
|
|
+ const [form] = Form.useForm();
|
|
|
+ const [formLoading, setFormLoading] = useState(false);
|
|
|
+ const [hardwareOptions, setHardwareOptions] = useState<{ label: string; value: number }[]>([]);
|
|
|
+ const [lockTypeTreeOptions, setLockTypeTreeOptions] = useState<any[]>([]);
|
|
|
+ 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<PadLockTypeVO>(
|
|
|
+ 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 = {
|
|
|
+ ...values,
|
|
|
+ lockId: editingPadLock?.lockId || editingPadLock?.id,
|
|
|
+ };
|
|
|
+
|
|
|
+ onSave(submitData);
|
|
|
+ } catch (error) {
|
|
|
+ // 表单验证失败
|
|
|
+ } finally {
|
|
|
+ setFormLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Modal
|
|
|
+ title={editingPadLock ? '编辑挂锁' : '新增挂锁'}
|
|
|
+ open={visible}
|
|
|
+ onOk={handleSubmit}
|
|
|
+ onCancel={onCancel}
|
|
|
+ okText="确定"
|
|
|
+ cancelText="取消"
|
|
|
+ confirmLoading={formLoading}
|
|
|
+ width={600}
|
|
|
+ >
|
|
|
+ <Form
|
|
|
+ form={form}
|
|
|
+ layout="vertical"
|
|
|
+ className="mt-4"
|
|
|
+ >
|
|
|
+ <Form.Item
|
|
|
+ label="所属硬件"
|
|
|
+ name="hardwareId"
|
|
|
+ >
|
|
|
<Select
|
|
|
- value={formData.enableFlag}
|
|
|
- onChange={(value) => setFormData({ ...formData, enableFlag: value })}
|
|
|
- style={{ width: '100%' }}
|
|
|
- >
|
|
|
- <Select.Option value="1">启用</Select.Option>
|
|
|
- <Select.Option value="0">禁用</Select.Option>
|
|
|
- </Select>
|
|
|
- </div>
|
|
|
- <div>
|
|
|
- <label className="block text-sm text-gray-700 mb-2">备注</label>
|
|
|
- <Input.TextArea
|
|
|
- value={formData.remark}
|
|
|
- onChange={(e) => setFormData({ ...formData, remark: e.target.value })}
|
|
|
- placeholder="请输入备注"
|
|
|
- rows={3}
|
|
|
+ placeholder="请选择所属硬件"
|
|
|
+ allowClear
|
|
|
+ options={hardwareOptions}
|
|
|
/>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="挂锁类型"
|
|
|
+ name="lockTypeId"
|
|
|
+ >
|
|
|
+ <TreeSelect
|
|
|
+ treeData={lockTypeTreeOptions}
|
|
|
+ placeholder="请选择挂锁类型"
|
|
|
+ allowClear
|
|
|
+ treeDefaultExpandAll
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="挂锁名称"
|
|
|
+ name="lockName"
|
|
|
+ rules={[{ required: true, message: '请输入挂锁名称' }]}
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入挂锁名称" />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="挂锁NFC"
|
|
|
+ name="lockNfc"
|
|
|
+ rules={[{ required: true, message: '请输入挂锁NFC' }]}
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入挂锁NFC" />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="挂锁型号"
|
|
|
+ name="lockSpec"
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入挂锁型号" />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="状态"
|
|
|
+ name="exStatus"
|
|
|
+ >
|
|
|
+ <Radio.Group>
|
|
|
+ {statusOptions.map(option => (
|
|
|
+ <Radio key={option.value} value={option.value}>
|
|
|
+ {option.label}
|
|
|
+ </Radio>
|
|
|
+ ))}
|
|
|
+ </Radio.Group>
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="备注"
|
|
|
+ name="exRemark"
|
|
|
+ >
|
|
|
+ <Input.TextArea placeholder="请输入备注" rows={3} />
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
</Modal>
|
|
|
);
|
|
|
}
|