FaceOrFingerForm.tsx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import React, { useState, useEffect, useImperativeHandle, forwardRef } from 'react';
  2. import { Modal, Table, Button, Image, message, Space } from 'antd';
  3. import { DeleteOutlined, ZoomInOutlined } from '@ant-design/icons';
  4. import { userCharacteristicApi } from '../../api/user/characteristic';
  5. import { UserCharacteristic } from '../../types';
  6. import { formatDateTimeFull } from '../../utils/formatTime';
  7. import type { ColumnsType } from 'antd/es/table';
  8. interface FaceOrFingerFormProps {
  9. onSuccess?: () => void;
  10. }
  11. export interface FaceOrFingerFormRef {
  12. open: (type: string, id: number, row: any) => void;
  13. }
  14. const FaceOrFingerForm = forwardRef<FaceOrFingerFormRef, FaceOrFingerFormProps>(({ onSuccess }, ref) => {
  15. const [dialogVisible, setDialogVisible] = useState(false);
  16. const [dialogTitle, setDialogTitle] = useState('');
  17. const [formLoading, setFormLoading] = useState(false);
  18. const [tableData, setTableData] = useState<UserCharacteristic[]>([]);
  19. const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
  20. const [total, setTotal] = useState(0);
  21. const [queryParams, setQueryParams] = useState({
  22. pageNo: 1,
  23. pageSize: 10,
  24. });
  25. const [userId, setUserId] = useState<number>();
  26. const [userType, setUserType] = useState<string>();
  27. // 打开弹窗
  28. const open = async (type: string, id: number, row: any) => {
  29. setDialogVisible(true);
  30. setDialogTitle(type === 'finger' ? '人员指纹数据' : '人员面部数据');
  31. setUserId(row.id);
  32. setUserType(type === 'finger' ? '1' : '2');
  33. setQueryParams({ pageNo: 1, pageSize: 10 });
  34. setTotal(0);
  35. setSelectedRowKeys([]);
  36. await getFaceOrFingerList();
  37. };
  38. useImperativeHandle(ref, () => ({
  39. open,
  40. }));
  41. // 获取指纹或人脸列表
  42. const getFaceOrFingerList = async () => {
  43. if (!userId || !userType) return;
  44. setFormLoading(true);
  45. try {
  46. const response = await userCharacteristicApi.getUserType({
  47. pageNo: queryParams.pageNo,
  48. pageSize: queryParams.pageSize,
  49. userId: userId,
  50. type: userType,
  51. });
  52. setTableData(response.list || []);
  53. setTotal(response.total || 0);
  54. } catch (error: any) {
  55. message.error(error.message || '获取数据失败');
  56. } finally {
  57. setFormLoading(false);
  58. }
  59. };
  60. useEffect(() => {
  61. if (dialogVisible && userId && userType) {
  62. getFaceOrFingerList();
  63. }
  64. }, [queryParams.pageNo, queryParams.pageSize, dialogVisible]);
  65. // 删除指纹或人脸
  66. const handleDelete = async (id?: number) => {
  67. const idsToDelete = id ? [id] : selectedRowKeys.map(key => Number(key));
  68. if (idsToDelete.length === 0) {
  69. message.warning('请选择要删除的数据');
  70. return;
  71. }
  72. Modal.confirm({
  73. title: '确认删除',
  74. content: `确定要删除选中的 ${idsToDelete.length} 条数据吗?`,
  75. okText: '确定',
  76. cancelText: '取消',
  77. onOk: async () => {
  78. try {
  79. // 逐个删除
  80. for (const deleteId of idsToDelete) {
  81. await userCharacteristicApi.deleteUserFaceOrFinger(deleteId);
  82. }
  83. message.success('删除成功');
  84. await getFaceOrFingerList();
  85. setSelectedRowKeys([]);
  86. onSuccess?.();
  87. } catch (error: any) {
  88. message.error(error.message || '删除失败');
  89. }
  90. },
  91. });
  92. };
  93. // 表格列配置
  94. const columns: ColumnsType<UserCharacteristic> = [
  95. {
  96. title: '序号',
  97. width: 80,
  98. align: 'center',
  99. render: (_: any, __: UserCharacteristic, index: number) => {
  100. return (queryParams.pageNo - 1) * queryParams.pageSize + index + 1;
  101. },
  102. },
  103. {
  104. title: dialogTitle === '人员指纹数据' ? '指纹' : '人脸',
  105. align: 'center',
  106. render: (_: any, record: UserCharacteristic) => (
  107. <div style={{ position: 'relative', display: 'inline-block' }}>
  108. <Image
  109. width={60}
  110. height={60}
  111. src={record.imageUrl}
  112. alt={dialogTitle === '人员指纹数据' ? '指纹' : '人脸'}
  113. preview={{
  114. mask: <ZoomInOutlined />,
  115. }}
  116. style={{ borderRadius: 4, border: '1px solid #dcdfe6' }}
  117. />
  118. </div>
  119. ),
  120. },
  121. {
  122. title: '创建时间',
  123. align: 'center',
  124. width: 180,
  125. render: (_: any, record: UserCharacteristic) => formatDateTimeFull(record.createTime),
  126. },
  127. {
  128. title: '操作',
  129. align: 'center',
  130. width: 120,
  131. render: (_: any, record: UserCharacteristic) => (
  132. <Button
  133. type="link"
  134. danger
  135. icon={<DeleteOutlined />}
  136. onClick={() => handleDelete(record.id)}
  137. >
  138. 删除
  139. </Button>
  140. ),
  141. },
  142. ];
  143. // 行选择配置
  144. const rowSelection = {
  145. selectedRowKeys,
  146. onChange: (selectedKeys: React.Key[]) => {
  147. setSelectedRowKeys(selectedKeys);
  148. },
  149. };
  150. return (
  151. <Modal
  152. title={dialogTitle}
  153. open={dialogVisible}
  154. onCancel={() => setDialogVisible(false)}
  155. footer={[
  156. <Button key="close" onClick={() => setDialogVisible(false)}>
  157. 关闭
  158. </Button>,
  159. ]}
  160. width={800}
  161. destroyOnClose
  162. >
  163. <div style={{ marginBottom: 16, display: 'flex', justifyContent: 'flex-end' }}>
  164. <Button
  165. type="primary"
  166. danger
  167. icon={<DeleteOutlined />}
  168. onClick={() => handleDelete()}
  169. disabled={selectedRowKeys.length === 0 || formLoading}
  170. >
  171. 删除
  172. </Button>
  173. </div>
  174. <Table
  175. columns={columns}
  176. dataSource={tableData}
  177. rowKey="id"
  178. loading={formLoading}
  179. pagination={false}
  180. rowSelection={rowSelection}
  181. scroll={{ y: 450 }}
  182. bordered
  183. />
  184. {/* 分页 */}
  185. {total > 0 && (
  186. <div style={{ marginTop: 16, paddingTop: 16, borderTop: '1px solid #f0f0f0', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
  187. <div style={{ fontSize: 14, color: '#666' }}>
  188. 共 <span style={{ color: '#1890ff', fontWeight: 500 }}>{total}</span> 条记录
  189. </div>
  190. <Space>
  191. <Button
  192. onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo - 1 })}
  193. disabled={queryParams.pageNo <= 1}
  194. >
  195. 上一页
  196. </Button>
  197. <span style={{ padding: '0 16px', fontSize: 14, color: '#666' }}>
  198. {queryParams.pageNo} / {Math.ceil(total / queryParams.pageSize)}
  199. </span>
  200. <Button
  201. onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo + 1 })}
  202. disabled={queryParams.pageNo >= Math.ceil(total / queryParams.pageSize)}
  203. >
  204. 下一页
  205. </Button>
  206. </Space>
  207. </div>
  208. )}
  209. </Modal>
  210. );
  211. });
  212. FaceOrFingerForm.displayName = 'FaceOrFingerForm';
  213. export default FaceOrFingerForm;