فهرست منبع

Initial commit

D8D AI 1 سال پیش
کامیت
bc6f19df8f
5فایلهای تغییر یافته به همراه299 افزوده شده و 0 حذف شده
  1. 21 0
      .gitignore
  2. 29 0
      package.json
  3. 21 0
      src/App.tsx
  4. 85 0
      src/components/MemberForm.tsx
  5. 143 0
      src/components/MemberList.tsx

+ 21 - 0
.gitignore

@@ -0,0 +1,21 @@
+node_modules/
+.DS_Store
+*.log
+dist/
+build/
+coverage/
+.env
+.env.local
+.env.*.local
+.cache/
+.next/
+.nuxt/
+.vuepress/dist
+.serverless/
+.idea/
+.vscode/
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 29 - 0
package.json

@@ -0,0 +1,29 @@
+{
+  "name": "member-management-system",
+  "private": true,
+  "version": "0.1.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "tsc && vite build",
+    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "antd": "^5.14.0",
+    "react": "^18.3.1",
+    "react-dom": "^18.3.1",
+    "moment": "^2.30.1"
+  },
+  "devDependencies": {
+    "@types/react": "^18.3.5",
+    "@types/react-dom": "^18.3.0",
+    "@vitejs/plugin-react": "^4.3.1",
+    "autoprefixer": "^10.4.18",
+    "eslint": "^9.9.1",
+    "postcss": "^8.4.35",
+    "tailwindcss": "^3.4.1",
+    "typescript": "^5.5.3",
+    "vite": "^5.4.2"
+  }
+}

+ 21 - 0
src/App.tsx

@@ -0,0 +1,21 @@
+import React from 'react';
+import MemberList from './components/MemberList';
+import { Layout, Typography } from 'antd';
+
+const { Header, Content } = Layout;
+const { Title } = Typography;
+
+function App() {
+  return (
+    <Layout className="min-h-screen">
+      <Header className="bg-white">
+        <Title level={3}>会友信息考勤打卡系统</Title>
+      </Header>
+      <Content className="p-6">
+        <MemberList />
+      </Content>
+    </Layout>
+  );
+}
+
+export default App;

+ 85 - 0
src/components/MemberForm.tsx

@@ -0,0 +1,85 @@
+import React from 'react';
+import { Modal, Form, Input, DatePicker, Select, InputNumber, Switch } from 'antd';
+import moment from 'moment';
+
+const { Option } = Select;
+
+interface MemberFormProps {
+  visible: boolean;
+  onCancel: () => void;
+  onSave: (member: any) => void;
+  member?: any;
+}
+
+const MemberForm: React.FC<MemberFormProps> = ({ visible, onCancel, onSave, member }) => {
+  const [form] = Form.useForm();
+
+  React.useEffect(() => {
+    if (member) {
+      form.setFieldsValue({
+        ...member,
+        birthDate: moment(member.birthDate),
+        baptismDate: member.baptismDate ? moment(member.baptismDate) : null,
+      });
+    } else {
+      form.resetFields();
+    }
+  }, [member, form]);
+
+  const handleOk = () => {
+    form.validateFields().then(values => {
+      onSave({
+        ...values,
+        birthDate: values.birthDate.format('YYYY-MM-DD'),
+        baptismDate: values.baptismDate ? values.baptismDate.format('YYYY-MM-DD') : null,
+      });
+    });
+  };
+
+  return (
+    <Modal
+      visible={visible}
+      title={member ? "编辑会员信息" : "添加会员信息"}
+      onCancel={onCancel}
+      onOk={handleOk}
+    >
+      <Form form={form} layout="vertical">
+        <Form.Item name="name" label="姓名" rules={[{ required: true }]}>
+          <Input />
+        </Form.Item>
+        <Form.Item name="gender" label="性别" rules={[{ required: true }]}>
+          <Select>
+            <Option value="男">男</Option>
+            <Option value="女">女</Option>
+          </Select>
+        </Form.Item>
+        <Form.Item name="age" label="年龄" rules={[{ required: true }]}>
+          <InputNumber min={0} max={150} />
+        </Form.Item>
+        <Form.Item name="birthDate" label="出生日期" rules={[{ required: true }]}>
+          <DatePicker />
+        </Form.Item>
+        <Form.Item name="contact" label="联系方式" rules={[{ required: true }]}>
+          <Input />
+        </Form.Item>
+        <Form.Item name="occupation" label="职业">
+          <Input />
+        </Form.Item>
+        <Form.Item name="servicePosition" label="服侍岗位">
+          <Input />
+        </Form.Item>
+        <Form.Item name="isBaptized" label="是否受洗" valuePropName="checked">
+          <Switch />
+        </Form.Item>
+        <Form.Item name="baptismDate" label="受洗日期">
+          <DatePicker />
+        </Form.Item>
+        <Form.Item name="notes" label="备注">
+          <Input.TextArea />
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+};
+
+export default MemberForm;

+ 143 - 0
src/components/MemberList.tsx

@@ -0,0 +1,143 @@
+import React, { useState, useEffect } from 'react';
+import { Table, Button, Input } from 'antd';
+import { PlusOutlined, EditOutlined, DeleteOutlined, DownloadOutlined } from '@ant-design/icons';
+import MemberForm from './MemberForm';
+
+interface Member {
+  id: number;
+  name: string;
+  gender: string;
+  age: number;
+  birthDate: string;
+  contact: string;
+  occupation: string;
+  servicePosition: string;
+  isBaptized: boolean;
+  baptismDate?: string;
+  notes?: string;
+}
+
+const MemberList: React.FC = () => {
+  const [members, setMembers] = useState<Member[]>([]);
+  const [isModalVisible, setIsModalVisible] = useState(false);
+  const [editingMember, setEditingMember] = useState<Member | null>(null);
+  const [searchTerm, setSearchTerm] = useState('');
+
+  useEffect(() => {
+    // 这里应该从API获取会员数据
+    // 暂时使用模拟数据
+    const mockMembers: Member[] = [
+      {
+        id: 1,
+        name: '张三',
+        gender: '男',
+        age: 30,
+        birthDate: '1994-01-01',
+        contact: '13800138000',
+        occupation: '工程师',
+        servicePosition: '主日学老师',
+        isBaptized: true,
+        baptismDate: '2020-05-01',
+        notes: '热心服侍'
+      },
+      // ... 更多模拟数据
+    ];
+    setMembers(mockMembers);
+  }, []);
+
+  const columns = [
+    {
+      title: '姓名',
+      dataIndex: 'name',
+      key: 'name',
+    },
+    {
+      title: '性别',
+      dataIndex: 'gender',
+      key: 'gender',
+    },
+    {
+      title: '年龄',
+      dataIndex: 'age',
+      key: 'age',
+    },
+    {
+      title: '联系方式',
+      dataIndex: 'contact',
+      key: 'contact',
+    },
+    {
+      title: '操作',
+      key: 'action',
+      render: (text: string, record: Member) => (
+        <span>
+          <Button icon={<EditOutlined />} onClick={() => handleEdit(record)}>编辑</Button>
+          <Button icon={<DeleteOutlined />} onClick={() => handleDelete(record.id)}>删除</Button>
+        </span>
+      ),
+    },
+  ];
+
+  const handleAdd = () => {
+    setEditingMember(null);
+    setIsModalVisible(true);
+  };
+
+  const handleEdit = (member: Member) => {
+    setEditingMember(member);
+    setIsModalVisible(true);
+  };
+
+  const handleDelete = (id: number) => {
+    setMembers(members.filter(member => member.id !== id));
+  };
+
+  const handleSave = (member: Member) => {
+    if (editingMember) {
+      setMembers(members.map(m => m.id === member.id ? member : m));
+    } else {
+      setMembers([...members, { ...member, id: members.length + 1 }]);
+    }
+    setIsModalVisible(false);
+  };
+
+  const handleSearch = (value: string) => {
+    setSearchTerm(value);
+  };
+
+  const filteredMembers = members.filter(member => 
+    member.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
+    member.contact.includes(searchTerm)
+  );
+
+  return (
+    <div>
+      <div style={{ marginBottom: 16 }}>
+        <Button icon={<PlusOutlined />} onClick={handleAdd}>添加会员</Button>
+        <Button icon={<DownloadOutlined />}>导出Excel</Button>
+        <Input.Search
+          placeholder="搜索姓名或联系方式"
+          onSearch={handleSearch}
+          style={{ width: 200, float: 'right' }}
+        />
+      </div>
+      <Table 
+        columns={columns} 
+        dataSource={filteredMembers}
+        rowKey="id"
+        pagination={{
+          total: filteredMembers.length,
+          showTotal: (total) => `共 ${total} 条记录`,
+        }}
+      />
+      <MemberForm
+        visible={isModalVisible}
+        onCancel={() => setIsModalVisible(false)}
+        onSave={handleSave}
+        member={editingMember}
+      />
+    </div>
+  );
+};
+
+export default MemberList;