| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146 |
- 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<PadLockVO[]>([]);
- const [total, setTotal] = useState(0);
- const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
- const [queryParams, setQueryParams] = useState<PageParam>({
- pageNo: 1,
- pageSize: 10,
- lockCode: undefined,
- 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: -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) => {
- 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<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 {
- 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: <ExclamationCircleOutlined />,
- content: (
- <div>
- <p>确定要删除选中的 {ids.length} 条挂锁数据吗?</p>
- <p style={{ color: '#ff4d4f', marginTop: '8px' }}>删除后无法恢复,请谨慎操作!</p>
- </div>
- ),
- 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: <ExclamationCircleOutlined />,
- content: (
- <div>
- <p>确定要删除挂锁类型 <strong>"{name || '该类型'}"</strong> 吗?</p>
- <p style={{ color: '#ff4d4f', marginTop: '8px' }}>删除后无法恢复,请谨慎操作!</p>
- </div>
- ),
- 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<PadLockVO> = [
- {
- 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 (
- <Switch
- checked={status === '1'}
- disabled
- checkedChildren="启用"
- unCheckedChildren="禁用"
- />
- );
- },
- },
- {
- 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) => (
- <div className="flex items-center gap-2 justify-center">
- <PermissionWrapper permission="iscs:lock:update">
- <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>
- </PermissionWrapper>
- <PermissionWrapper permission="iscs:lock:delete">
- <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>
- </PermissionWrapper>
- </div>
- ),
- },
- ];
- // 挂锁类型表格列(树形表格)
- const padLockTypeColumns: ColumnsType<PadLockTypeVO> = [
- {
- title: '挂锁类型名称',
- dataIndex: 'lockTypeName',
- width: 200,
- align: 'center',
- },
- {
- title: '挂锁类型图标',
- dataIndex: 'lockTypeIcon',
- width: 120,
- align: 'center',
- render: (url: string) => {
- if (!url) return '-';
- return (
- <Image
- src={url}
- alt="图标"
- width={50}
- height={50}
- style={{ objectFit: 'cover' }}
- preview={{
- mask: '查看',
- }}
- />
- );
- },
- },
- {
- title: '挂锁类型图片',
- dataIndex: 'lockTypeImg',
- width: 120,
- align: 'center',
- render: (url: string) => {
- if (!url) return '-';
- return (
- <Image
- src={url}
- alt="图片"
- width={50}
- height={50}
- style={{ objectFit: 'cover' }}
- preview={{
- mask: '查看',
- }}
- />
- );
- },
- },
- {
- title: '挂锁型号',
- dataIndex: 'lockTypeSpec',
- width: 150,
- align: 'center',
- render: (spec: string) => spec || '-',
- },
- {
- title: '操作',
- width: 200,
- align: 'center',
- fixed: 'right',
- render: (_: any, record: PadLockTypeVO) => (
- <div className="flex items-center gap-2 justify-center">
- <PermissionWrapper permission="iscs:lock:update">
- <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>
- </PermissionWrapper>
- <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>
- ),
- },
- ];
- return (
- <div className="space-y-4">
- {viewMode === 'list' ? (
- <>
- {/* 挂锁列表 - 搜索栏和表格放在一起 */}
- <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm overflow-hidden">
- {/* 搜索栏 */}
- <div className="p-4 lg:p-5 border-b border-gray-200">
- <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
- {/* 搜索输入框 */}
- <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
- <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
- <label className="text-sm font-medium text-gray-700 whitespace-nowrap">挂锁名称:</label>
- <Input
- value={queryParams.lockName || ''}
- onChange={(e) => setQueryParams({ ...queryParams, lockName: e.target.value })}
- onPressEnter={handleQuery}
- placeholder="请输入挂锁名称"
- className="min-w-[150px] max-w-[200px]"
- allowClear
- />
- </div>
- </div>
- {/* 操作按钮组 */}
- <Space className="flex-shrink-0">
- <PermissionWrapper permission="iscs:lock:query">
- <Button
- type="primary"
- icon={<Search className="w-4 h-4" />}
- onClick={handleQuery}
- >
- 搜索
- </Button>
- </PermissionWrapper>
-
- <PermissionWrapper permission="iscs:lock:query">
- <Button
- icon={<RefreshCw className="w-4 h-4" />}
- onClick={resetQuery}
- >
- 重置
- </Button>
- </PermissionWrapper>
-
- <PermissionWrapper permission="iscs:lock:create">
- <Button
- type="primary"
- icon={<Plus className="w-4 h-4" />}
- onClick={() => openPadLockForm('create')}
- >
- 新增
- </Button>
- </PermissionWrapper>
-
- <PermissionWrapper permission="iscs:lock:delete">
- <Button
- danger
- icon={<Trash2 className="w-4 h-4" />}
- onClick={() => handleDelete()}
- disabled={selectedRowKeys.length === 0}
- >
- 批量删除
- </Button>
- </PermissionWrapper>
-
- <Button
- icon={<Settings className="w-4 h-4" />}
- onClick={() => setViewMode('type')}
- >
- 设置挂锁类型
- </Button>
- </Space>
- </div>
- </div>
- {/* 表格 */}
- <div className="min-w-0">
- <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>
- {/* 分页 */}
- {!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, pageNo: queryParams.pageNo! - 1 })}
- disabled={queryParams.pageNo! <= 1}
- >
- 上一页
- </Button>
- <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
- {queryParams.pageNo} / {Math.ceil(total / queryParams.pageSize!) || 1}
- </span>
- <Button
- onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo! + 1 })}
- disabled={queryParams.pageNo! >= Math.ceil(total / queryParams.pageSize!)}
- >
- 下一页
- </Button>
- </div>
- </div>
- </div>
- )}
- {/* 挂锁表单弹窗 */}
- <PadLockFormModal
- visible={showPadLockForm}
- editingPadLock={editingPadLock}
- onCancel={() => {
- setShowPadLockForm(false);
- setEditingPadLock(null);
- }}
- onSave={savePadLock}
- />
- </>
- ) : (
- <>
- {/* 挂锁类型 - 搜索栏和表格放在一起 */}
- <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-2 lg:gap-3 flex-wrap flex-1 min-w-0">
- <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
- <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="请输入类型名称"
- className="min-w-[150px] max-w-[200px]"
- allowClear
- />
- </div>
- </div>
- {/* 操作按钮组 */}
- <Space className="flex-shrink-0">
- <PermissionWrapper permission="iscs:lock:query">
- <Button
- type="primary"
- icon={<Search className="w-4 h-4" />}
- onClick={handleTypeQuery}
- >
- 搜索
- </Button>
- </PermissionWrapper>
-
- <PermissionWrapper permission="iscs:lock:query">
- <Button
- icon={<RefreshCw className="w-4 h-4" />}
- onClick={resetTypeQuery}
- >
- 重置
- </Button>
- </PermissionWrapper>
-
- <PermissionWrapper permission="iscs:lock:create">
- <Button
- type="primary"
- icon={<Plus className="w-4 h-4" />}
- onClick={() => openTypeForm('create')}
- >
- 新增
- </Button>
- </PermissionWrapper>
-
- <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}
- />
- )}
- </>
- )}
- </div>
- );
- }
- // 挂锁类型表单弹窗组件
- 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<any[]>([]);
- // 构建树形选择器数据
- useEffect(() => {
- 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));
- }
- }, [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 (
- <Modal
- title={editingType ? '编辑挂锁类型' : '新增挂锁类型'}
- open={visible}
- onOk={handleSubmit}
- onCancel={onCancel}
- okText={t('common.confirm')}
- cancelText={t('common.cancel')}
- confirmLoading={formLoading}
- width={600}
- >
- <Form
- form={form}
- layout="horizontal"
- labelCol={{ span: 6 }}
- wrapperCol={{ span: 18 }}
- 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"
- />
- </Form.Item>
-
- <Form.Item
- label="挂锁类型图片"
- name="lockTypeImg"
- >
- <UploadImg
- value={form.getFieldValue('lockTypeImg')}
- onChange={(value) => form.setFieldsValue({ lockTypeImg: value })}
- limit={1}
- height="100px"
- width="100px"
- />
- </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 { t } = useTranslation();
- 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 = editingPadLock ? {
- // 保留原始数据中的字段(这些字段用户不能编辑但需要传递)
- ...editingPadLock,
- // 用表单数据覆盖可编辑字段
- ...values,
- // 确保 id 和 lockId 都存在
- id: editingPadLock.id || editingPadLock.lockId,
- lockId: editingPadLock.lockId || editingPadLock.id,
- } : {
- // 新增模式下,只传递表单数据
- ...values,
- };
-
- onSave(submitData);
- } catch (error) {
- // 表单验证失败
- } finally {
- setFormLoading(false);
- }
- };
- return (
- <Modal
- title={editingPadLock ? '编辑挂锁' : '新增挂锁'}
- open={visible}
- onOk={handleSubmit}
- onCancel={onCancel}
- okText={t('common.confirm')}
- cancelText={t('common.cancel')}
- confirmLoading={formLoading}
- width={600}
- >
- <Form
- form={form}
- layout="horizontal"
- labelCol={{ span: 6 }}
- wrapperCol={{ span: 18 }}
- className="mt-4"
- >
- <Form.Item
- label="所属硬件"
- name="hardwareId"
- >
- <Select
- placeholder="请选择所属硬件"
- allowClear
- options={hardwareOptions}
- />
- </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>
- );
- }
|