Kaynağa Gözat

优化系统界面UI设计

Message ID: 4Dc9mwE
zyh 1 yıl önce
ebeveyn
işleme
f439eeca3e

+ 41 - 2
package-lock.json

@@ -1,16 +1,17 @@
 {
   "name": "member-management-system",
-  "version": "0.1.0",
+  "version": "0.0.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "member-management-system",
-      "version": "0.1.0",
+      "version": "0.0.0",
       "dependencies": {
         "axios": "^0.27.2",
         "react": "^18.2.0",
         "react-dom": "^18.2.0",
+        "react-router-dom": "^6.4.0",
         "xlsx": "^0.18.5"
       },
       "devDependencies": {
@@ -687,6 +688,14 @@
         "@jridgewell/sourcemap-codec": "^1.4.14"
       }
     },
+    "node_modules/@remix-run/router": {
+      "version": "1.21.0",
+      "resolved": "https://registry.npmmirror.com/@remix-run/router/-/router-1.21.0.tgz",
+      "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
     "node_modules/@types/prop-types": {
       "version": "15.7.13",
       "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.13.tgz",
@@ -1184,6 +1193,36 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/react-router": {
+      "version": "6.28.0",
+      "resolved": "https://registry.npmmirror.com/react-router/-/react-router-6.28.0.tgz",
+      "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==",
+      "dependencies": {
+        "@remix-run/router": "1.21.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=16.8"
+      }
+    },
+    "node_modules/react-router-dom": {
+      "version": "6.28.0",
+      "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-6.28.0.tgz",
+      "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==",
+      "dependencies": {
+        "@remix-run/router": "1.21.0",
+        "react-router": "6.28.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=16.8",
+        "react-dom": ">=16.8"
+      }
+    },
     "node_modules/rollup": {
       "version": "3.29.5",
       "resolved": "https://registry.npmmirror.com/rollup/-/rollup-3.29.5.tgz",

+ 3 - 1
package.json

@@ -13,7 +13,9 @@
     "react-dom": "^18.2.0",
     "axios": "^0.27.2",
     "react-router-dom": "^6.4.0",
-    "xlsx": "^0.18.5"
+    "xlsx": "^0.18.5",
+    "antd": "^4.24.8",
+    "@ant-design/icons": "^4.7.0"
   },
   "devDependencies": {
     "@types/react": "^18.0.28",

+ 47 - 36
src/App.jsx

@@ -1,43 +1,54 @@
-import React from 'react'
-import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom'
-import AttendanceSystem from './components/AttendanceSystem'
-import MemberList from './components/MemberList'
-import MemberForm from './components/MemberForm'
-import ExcelImportExport from './components/ExcelImportExport'
+import React from 'react';
+import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom';
+import { Layout, Menu } from 'antd';
+import {
+  HomeOutlined,
+  TeamOutlined,
+  UserAddOutlined,
+  CalendarOutlined,
+} from '@ant-design/icons';
+import AttendanceSystem from './components/AttendanceSystem';
+import MemberList from './components/MemberList';
+import MemberForm from './components/MemberForm';
+import Home from './components/Home';
+
+const { Header, Content, Footer } = Layout;
 
 function App() {
   return (
     <Router>
-      <div>
-        <nav>
-          <ul>
-            <li><Link to="/">首页</Link></li>
-            <li><Link to="/members">会友列表</Link></li>
-            <li><Link to="/add-member">添加会友</Link></li>
-            <li><Link to="/attendance">考勤系统</Link></li>
-          </ul>
-        </nav>
-
-        <h1>会友信息管理系统</h1>
-
-        <Routes>
-          <Route path="/" element={<Home />} />
-          <Route path="/members" element={<MemberList />} />
-          <Route path="/add-member" element={<MemberForm />} />
-          <Route path="/attendance" element={<AttendanceSystem />} />
-        </Routes>
-      </div>
+      <Layout className="layout" style={{ minHeight: '100vh' }}>
+        <Header>
+          <div className="logo" />
+          <Menu theme="dark" mode="horizontal" defaultSelectedKeys={['1']}>
+            <Menu.Item key="1" icon={<HomeOutlined />}>
+              <Link to="/">首页</Link>
+            </Menu.Item>
+            <Menu.Item key="2" icon={<TeamOutlined />}>
+              <Link to="/members">会友列表</Link>
+            </Menu.Item>
+            <Menu.Item key="3" icon={<UserAddOutlined />}>
+              <Link to="/add-member">添加会友</Link>
+            </Menu.Item>
+            <Menu.Item key="4" icon={<CalendarOutlined />}>
+              <Link to="/attendance">考勤系统</Link>
+            </Menu.Item>
+          </Menu>
+        </Header>
+        <Content style={{ padding: '0 50px' }}>
+          <div className="site-layout-content" style={{ padding: 24, minHeight: 280 }}>
+            <Routes>
+              <Route path="/" element={<Home />} />
+              <Route path="/members" element={<MemberList />} />
+              <Route path="/add-member" element={<MemberForm />} />
+              <Route path="/attendance" element={<AttendanceSystem />} />
+            </Routes>
+          </div>
+        </Content>
+        <Footer style={{ textAlign: 'center' }}>会友信息管理系统 ©2024 Created by Your Company</Footer>
+      </Layout>
     </Router>
-  )
-}
-
-function Home() {
-  return (
-    <div>
-      <h2>欢迎使用会友信息管理系统</h2>
-      <p>当前时间: 2024-10-29 11:55:35</p>
-    </div>
-  )
+  );
 }
 
-export default App
+export default App;

+ 26 - 16
src/components/AttendanceSystem.jsx

@@ -1,9 +1,13 @@
 import React, { useState, useEffect } from 'react';
+import { Card, List, Typography, Spin } from 'antd';
 import { deviceApi } from '../api/deviceApi';
 
+const { Title, Text } = Typography;
+
 const AttendanceSystem = () => {
   const [deviceInfo, setDeviceInfo] = useState(null);
   const [attendanceRecords, setAttendanceRecords] = useState([]);
+  const [loading, setLoading] = useState(true);
 
   useEffect(() => {
     fetchDeviceInfo();
@@ -27,27 +31,33 @@ const AttendanceSystem = () => {
       setAttendanceRecords(records);
     } catch (error) {
       console.error('获取考勤记录失败', error);
+    } finally {
+      setLoading(false);
     }
   };
 
   return (
     <div>
-      <h2>考勤系统</h2>
-      {deviceInfo && (
-        <div>
-          <h3>设备信息</h3>
-          <p>设备ID: {deviceInfo.deviceId}</p>
-          <p>设备名称: {deviceInfo.deviceName}</p>
-        </div>
-      )}
-      <h3>考勤记录</h3>
-      <ul>
-        {attendanceRecords.map((record, index) => (
-          <li key={index}>
-            人员ID: {record.personId}, 打卡时间: {record.timestamp}
-          </li>
-        ))}
-      </ul>
+      <Title level={2}>考勤系统</Title>
+      <Spin spinning={loading}>
+        {deviceInfo && (
+          <Card title="设备信息" style={{ marginBottom: 20 }}>
+            <p><Text strong>设备ID:</Text> {deviceInfo.deviceId}</p>
+            <p><Text strong>设备名称:</Text> {deviceInfo.deviceName}</p>
+          </Card>
+        )}
+        <Card title="考勤记录">
+          <List
+            dataSource={attendanceRecords}
+            renderItem={(record, index) => (
+              <List.Item key={index}>
+                <Text>人员ID: {record.personId}</Text>
+                <Text>打卡时间: {record.timestamp}</Text>
+              </List.Item>
+            )}
+          />
+        </Card>
+      </Spin>
     </div>
   );
 };

+ 25 - 5
src/components/ExcelImportExport.jsx

@@ -1,4 +1,6 @@
 import React from 'react';
+import { Button, Upload, message } from 'antd';
+import { UploadOutlined, DownloadOutlined } from '@ant-design/icons';
 import * as XLSX from 'xlsx';
 
 const ExcelImportExport = ({ members, onImport }) => {
@@ -9,8 +11,7 @@ const ExcelImportExport = ({ members, onImport }) => {
     XLSX.writeFile(workbook, "members.xlsx");
   };
 
-  const handleImport = (e) => {
-    const file = e.target.files[0];
+  const handleImport = (file) => {
     const reader = new FileReader();
     reader.onload = (event) => {
       const bstr = event.target.result;
@@ -23,10 +24,29 @@ const ExcelImportExport = ({ members, onImport }) => {
     reader.readAsBinaryString(file);
   };
 
+  const props = {
+    beforeUpload: (file) => {
+      const isXlsx = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
+      if (!isXlsx) {
+        message.error('只能上传 XLSX 文件!');
+      }
+      return isXlsx || Upload.LIST_IGNORE;
+    },
+    onChange: (info) => {
+      if (info.file.status !== 'uploading') {
+        handleImport(info.file.originFileObj);
+      }
+    },
+  };
+
   return (
-    <div>
-      <button onClick={handleExport}>导出 Excel</button>
-      <input type="file" onChange={handleImport} accept=".xlsx, .xls" />
+    <div style={{ marginBottom: 16 }}>
+      <Button onClick={handleExport} icon={<DownloadOutlined />} style={{ marginRight: 8 }}>
+        导出 Excel
+      </Button>
+      <Upload {...props} showUploadList={false}>
+        <Button icon={<UploadOutlined />}>导入 Excel</Button>
+      </Upload>
     </div>
   );
 };

+ 45 - 0
src/components/Home.jsx

@@ -0,0 +1,45 @@
+import React from 'react';
+import { Typography, Card, Row, Col, Statistic } from 'antd';
+import { UserOutlined, CalendarOutlined } from '@ant-design/icons';
+
+const { Title } = Typography;
+
+function Home() {
+  return (
+    <div>
+      <Title level={2}>欢迎使用会友信息管理系统</Title>
+      <p>当前时间: 2024-10-29 11:57:05</p>
+      <Row gutter={16} style={{ marginTop: 20 }}>
+        <Col span={8}>
+          <Card>
+            <Statistic
+              title="总会友数"
+              value={1128}
+              prefix={<UserOutlined />}
+            />
+          </Card>
+        </Col>
+        <Col span={8}>
+          <Card>
+            <Statistic
+              title="今日签到"
+              value={93}
+              prefix={<CalendarOutlined />}
+            />
+          </Card>
+        </Col>
+        <Col span={8}>
+          <Card>
+            <Statistic
+              title="本月新增会友"
+              value={28}
+              prefix={<UserOutlined />}
+            />
+          </Card>
+        </Col>
+      </Row>
+    </div>
+  );
+}
+
+export default Home;

+ 34 - 44
src/components/MemberForm.jsx

@@ -1,60 +1,50 @@
-import React, { useState } from 'react';
+import React from 'react';
+import { Form, Input, Button, message } from 'antd';
 import { deviceApi } from '../api/deviceApi';
 
 const MemberForm = () => {
-  const [formData, setFormData] = useState({
-    name: '',
-    contact: '',
-  });
+  const [form] = Form.useForm();
 
-  const handleChange = (e) => {
-    const { name, value } = e.target;
-    setFormData(prevState => ({
-      ...prevState,
-      [name]: value
-    }));
-  };
-
-  const handleSubmit = async (e) => {
-    e.preventDefault();
+  const onFinish = async (values) => {
     try {
-      await deviceApi.addPerson(formData);
-      alert('会友添加成功');
-      setFormData({ name: '', contact: '' });
+      await deviceApi.addPerson(values);
+      message.success('会友添加成功');
+      form.resetFields();
     } catch (error) {
       console.error('添加会友失败', error);
-      alert('添加会友失败,请重试');
+      message.error('添加会友失败,请重试');
     }
   };
 
   return (
     <div>
       <h2>添加会友</h2>
-      <form onSubmit={handleSubmit}>
-        <div>
-          <label htmlFor="name">姓名:</label>
-          <input
-            type="text"
-            id="name"
-            name="name"
-            value={formData.name}
-            onChange={handleChange}
-            required
-          />
-        </div>
-        <div>
-          <label htmlFor="contact">联系方式:</label>
-          <input
-            type="text"
-            id="contact"
-            name="contact"
-            value={formData.contact}
-            onChange={handleChange}
-            required
-          />
-        </div>
-        <button type="submit">添加</button>
-      </form>
+      <Form
+        form={form}
+        name="memberForm"
+        onFinish={onFinish}
+        layout="vertical"
+      >
+        <Form.Item
+          name="name"
+          label="姓名"
+          rules={[{ required: true, message: '请输入姓名' }]}
+        >
+          <Input />
+        </Form.Item>
+        <Form.Item
+          name="contact"
+          label="联系方式"
+          rules={[{ required: true, message: '请输入联系方式' }]}
+        >
+          <Input />
+        </Form.Item>
+        <Form.Item>
+          <Button type="primary" htmlType="submit">
+            添加
+          </Button>
+        </Form.Item>
+      </Form>
     </div>
   );
 };

+ 31 - 20
src/components/MemberList.jsx

@@ -1,51 +1,62 @@
 import React, { useState, useEffect } from 'react';
+import { Table, Button, message } from 'antd';
 import { deviceApi } from '../api/deviceApi';
 import ExcelImportExport from './ExcelImportExport';
 
 const MemberList = () => {
   const [members, setMembers] = useState([]);
+  const [loading, setLoading] = useState(false);
 
   useEffect(() => {
     fetchMembers();
   }, []);
 
   const fetchMembers = async () => {
+    setLoading(true);
     try {
-      // 假设 deviceApi.getAllPersons() 方法存在
       const data = await deviceApi.getAllPersons();
       setMembers(data);
     } catch (error) {
       console.error('获取会友列表失败', error);
+      message.error('获取会友列表失败');
+    } finally {
+      setLoading(false);
     }
   };
 
   const handleImport = (data) => {
     setMembers(data);
-    // 这里可以添加将导入的数据保存到后端的逻辑
+    message.success('数据导入成功');
   };
 
+  const columns = [
+    {
+      title: 'ID',
+      dataIndex: 'personId',
+      key: 'personId',
+    },
+    {
+      title: '姓名',
+      dataIndex: 'name',
+      key: 'name',
+    },
+    {
+      title: '联系方式',
+      dataIndex: 'contact',
+      key: 'contact',
+    },
+  ];
+
   return (
     <div>
       <h2>会友列表</h2>
       <ExcelImportExport members={members} onImport={handleImport} />
-      <table>
-        <thead>
-          <tr>
-            <th>ID</th>
-            <th>姓名</th>
-            <th>联系方式</th>
-          </tr>
-        </thead>
-        <tbody>
-          {members.map((member) => (
-            <tr key={member.personId}>
-              <td>{member.personId}</td>
-              <td>{member.name}</td>
-              <td>{member.contact}</td>
-            </tr>
-          ))}
-        </tbody>
-      </table>
+      <Table
+        columns={columns}
+        dataSource={members}
+        rowKey="personId"
+        loading={loading}
+      />
     </div>
   );
 };