diff --git a/src/components/BatchRegister.tsx b/src/components/BatchRegister.tsx new file mode 100644 index 0000000..a518f0e --- /dev/null +++ b/src/components/BatchRegister.tsx @@ -0,0 +1,143 @@ +import React from 'react' +import { Layout, Collapse, Form, Input, Button, Space, message, Spin } from 'antd' +import type { FormInstance } from 'antd' +import PeopleForm from './PeopleForm.tsx' +import { createPeoplesBatch, type People } from '../apis' + +const { Panel } = Collapse as any +const { Content } = Layout + +type FormItem = { id: string } + +const BatchRegister: React.FC = () => { + const [commonForm] = Form.useForm() + const [items, setItems] = React.useState([{ id: `${Date.now()}-${Math.random()}` }]) + const instancesRef = React.useRef>({}) + const [loading, setLoading] = React.useState(false) + + const addItem = () => { + if (loading) return + setItems((arr) => [...arr, { id: `${Date.now()}-${Math.random()}` }]) + } + + const removeItem = (id: string) => { + if (loading) return + setItems((arr) => arr.filter((x) => x.id !== id)) + delete instancesRef.current[id] + } + + const buildPeople = (values: any, common: any): People => { + return { + name: values.name, + contact: values.contact || common.contact || undefined, + gender: values.gender, + age: values.age, + height: values.height || undefined, + marital_status: values.marital_status || undefined, + introduction: values.introduction || {}, + match_requirement: values.match_requirement || undefined, + cover: values.cover || undefined, + } + } + + const handleSubmit = async () => { + if (loading) return + try { + setLoading(true) + const common = await commonForm.validateFields().catch(() => ({})) + const ids = items.map((x) => x.id) + const forms = ids.map((id) => instancesRef.current[id]).filter(Boolean) + if (forms.length !== ids.length) { + setLoading(false) + message.error('表单未就绪') + return + } + const allValues: any[] = [] + for (const f of forms) { + try { + const v = await f.validateFields() + allValues.push(v) + } catch (err: any) { + setLoading(false) + message.error('请完善全部表单后再提交') + return + } + } + const payload: People[] = allValues.map((v) => buildPeople(v, common)) + const res = await createPeoplesBatch(payload) + const failedIdx: number[] = [] + res.forEach((r, i) => { + if (!r || r.error_code !== 0) failedIdx.push(i) + }) + const success = res.length - failedIdx.length + if (success > 0) message.success(`成功提交 ${success} 条`) + if (failedIdx.length > 0) { + message.error(`有 ${failedIdx.length} 条提交失败,请检查后重试`) + setItems((prev) => prev.filter((_, i) => failedIdx.includes(i))) + } else { + setItems([{ id: `${Date.now()}-${Math.random()}` }]) + commonForm.resetFields() + } + } catch (e: any) { + message.error('提交失败') + } finally { + setLoading(false) + } + } + + return ( + +
+ + +
+ + + +
+
+
+ +
+ + x.id)}> + {items.map((item, idx) => ( + removeItem(item.id)} disabled={loading}> + 删除 + + } + > + (instancesRef.current[item.id] = f)} + /> + + ))} + + +
+ + + + +
+ + {loading && ( +
+
+ +
+
+ )} +
+ + ) +} + +export default BatchRegister \ No newline at end of file diff --git a/src/components/LayoutWrapper.tsx b/src/components/LayoutWrapper.tsx index fb35b51..90d2fe1 100644 --- a/src/components/LayoutWrapper.tsx +++ b/src/components/LayoutWrapper.tsx @@ -4,6 +4,7 @@ import { Routes, Route, useNavigate, useLocation } from 'react-router-dom'; import SiderMenu from './SiderMenu.tsx'; import MainContent from './MainContent.tsx'; import ResourceList from './ResourceList.tsx'; +import BatchRegister from './BatchRegister.tsx'; import TopBar from './TopBar.tsx'; import '../styles/base.css'; import '../styles/layout.css'; @@ -21,6 +22,8 @@ const LayoutWrapper: React.FC = () => { switch (path) { case '/resources': return 'menu1'; + case '/batch-register': + return 'batch'; case '/menu2': return 'menu2'; default: @@ -35,6 +38,9 @@ const LayoutWrapper: React.FC = () => { case 'home': navigate('/'); break; + case 'batch': + navigate('/batch-register'); + break; case 'menu1': navigate('/resources'); break; @@ -77,6 +83,10 @@ const LayoutWrapper: React.FC = () => { /> } /> + } + /> = ({ onNavigate, selectedKey, mobileOpen, onMob }, [selectedKey]); const items = [ - { key: 'home', label: '注册', icon: }, - { key: 'menu1', label: '列表', icon: }, + { key: 'home', label: '录入资源', icon: }, + { key: 'batch', label: '批量录入', icon: }, + { key: 'menu1', label: '资源列表', icon: }, ]; // 移动端:使用 Drawer 覆盖主内容