hikvision-web 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,174 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { Table, Button, Input, Space, Modal, message, Select } from 'antd';
3
+ import { SearchOutlined, PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons';
4
+ import { useAppStore } from '../store/appStore';
5
+ import { personApi } from '../services/api';
6
+ import { Link } from 'react-router-dom';
7
+
8
+ const { Search } = Input;
9
+
10
+ export default function PersonList() {
11
+ const {
12
+ persons,
13
+ personsTotal,
14
+ isPersonLoading,
15
+ loadPersons,
16
+ } = useAppStore();
17
+
18
+ const [searchName, setSearchName] = useState('');
19
+ const [page, setPage] = useState(1);
20
+ const [pageSize, setPageSize] = useState(20);
21
+ const [deleteModal, setDeleteModal] = useState<{ visible: boolean; personId?: string; personName?: string }>({ visible: false });
22
+
23
+ useEffect(() => {
24
+ const timer = setTimeout(() => {
25
+ loadPersons({ pageNo: page, pageSize, personName: searchName });
26
+ }, 300);
27
+ return () => clearTimeout(timer);
28
+ }, [page, pageSize, searchName]);
29
+
30
+ const handleSearch = () => {
31
+ setPage(1);
32
+ loadPersons({ pageNo: 1, pageSize, personName: searchName });
33
+ };
34
+
35
+ const handleDelete = async () => {
36
+ if (!deleteModal.personId) return;
37
+ try {
38
+ await personApi.delete(deleteModal.personId);
39
+ message.success('删除成功');
40
+ setDeleteModal({ visible: false });
41
+ loadPersons({ pageNo: page, pageSize, personName: searchName });
42
+ } catch (error: any) {
43
+ message.error(`删除失败: ${error.message || error}`);
44
+ }
45
+ };
46
+
47
+ const columns = [
48
+ {
49
+ title: '人员 ID',
50
+ dataIndex: 'personId',
51
+ key: 'personId',
52
+ width: 200,
53
+ render: (text: string) => <code style={{ fontSize: 12 }}>{text}</code>,
54
+ },
55
+ {
56
+ title: '姓名',
57
+ dataIndex: 'personName',
58
+ key: 'personName',
59
+ width: 100,
60
+ },
61
+ {
62
+ title: '性别',
63
+ dataIndex: 'gender',
64
+ key: 'gender',
65
+ width: 60,
66
+ render: (g: string | number) => {
67
+ const map: Record<string, string> = { '1': '男', '2': '女' };
68
+ return map[String(g)] || '未知';
69
+ },
70
+ },
71
+ {
72
+ title: '组织',
73
+ dataIndex: 'orgName',
74
+ key: 'orgName',
75
+ width: 150,
76
+ },
77
+ {
78
+ title: '手机号',
79
+ dataIndex: 'phoneNo',
80
+ key: 'phoneNo',
81
+ width: 120,
82
+ },
83
+ {
84
+ title: '工号',
85
+ dataIndex: 'jobNo',
86
+ key: 'jobNo',
87
+ width: 100,
88
+ },
89
+ {
90
+ title: '邮箱',
91
+ dataIndex: 'email',
92
+ key: 'email',
93
+ width: 150,
94
+ },
95
+ {
96
+ title: '操作',
97
+ key: 'actions',
98
+ width: 150,
99
+ render: (_: any, record: any) => (
100
+ <Space size="small">
101
+ <Link to={`/person/detail/${record.personId}`}>
102
+ <Button type="link" icon={<EyeOutlined />} size="small">详情</Button>
103
+ </Link>
104
+ <Link to={`/person/edit/${record.personId}`}>
105
+ <Button type="link" icon={<EditOutlined />} size="small">编辑</Button>
106
+ </Link>
107
+ <Button
108
+ type="link"
109
+ danger
110
+ icon={<DeleteOutlined />}
111
+ size="small"
112
+ onClick={() => setDeleteModal({ visible: true, personId: record.personId, personName: record.personName })}
113
+ >
114
+ 删除
115
+ </Button>
116
+ </Space>
117
+ ),
118
+ },
119
+ ];
120
+
121
+ return (
122
+ <div>
123
+ <div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
124
+ <h2 className="page-title" style={{ marginBottom: 0 }}>人员管理</h2>
125
+ <Link to="/person/add">
126
+ <Button type="primary" icon={<PlusOutlined />}>添加人员</Button>
127
+ </Link>
128
+ </div>
129
+
130
+ <div style={{ marginBottom: 16, display: 'flex', gap: 8 }}>
131
+ <Search
132
+ placeholder="搜索姓名"
133
+ value={searchName}
134
+ onChange={(e) => setSearchName(e.target.value)}
135
+ onPressEnter={handleSearch}
136
+ style={{ width: 200 }}
137
+ />
138
+ <Button icon={<SearchOutlined />} onClick={handleSearch}>搜索</Button>
139
+ <Select
140
+ value={pageSize}
141
+ onChange={(v) => { setPageSize(v); setPage(1); }}
142
+ style={{ width: 100 }}
143
+ options={[{ value: 10, label: '10/页' }, { value: 20, label: '20/页' }, { value: 50, label: '50/页' }]}
144
+ />
145
+ </div>
146
+
147
+ <Table
148
+ columns={columns}
149
+ dataSource={persons}
150
+ rowKey="personId"
151
+ loading={isPersonLoading}
152
+ pagination={{ total: personsTotal,
153
+ current: page,
154
+ pageSize,
155
+ showSizeChanger: true,
156
+ showTotal: (t) => `共 ${t} 条`,
157
+ onChange: (p, ps) => { setPage(p); setPageSize(ps); },
158
+ }}
159
+ />
160
+
161
+ <Modal
162
+ title="确认删除"
163
+ open={deleteModal.visible}
164
+ onOk={handleDelete}
165
+ onCancel={() => setDeleteModal({ visible: false })}
166
+ okText="确认删除"
167
+ okButtonProps={{ danger: true }}
168
+ >
169
+ <p>确定要删除人员 <strong>{deleteModal.personName}</strong> 吗?</p>
170
+ <p style={{ color: '#ff4d4f' }}>此操作不可撤销!</p>
171
+ </Modal>
172
+ </div>
173
+ );
174
+ }