Browse Source

注册页面集成到框架中,点击菜单项‘注册’后,可以显示注册页面;注册页面适应多屏幕

dengdx 2 months ago
parent
commit
8f9e2f988e

+ 18 - 2
src/layouts/BasicLayout.tsx

@@ -8,6 +8,9 @@ import logo from '../assets/imgs/logo-v3.jpg';
 import NavbarFloat from './NavbarFloat';
 import NavMenu from './NavMenu';
 import StatusBar, { StatusBarProps,DiskStatusType } from './StateBar';
+import AboutPage from '@/pages/demo/AboutPage';
+import HomePage from '@/pages/demo/HomePage';
+import RegisterPage from '@/pages/patient/register';
 
 // import { Link } from 'react-router-dom';
 // import { MenuOutlined } from '@ant-design/icons';
@@ -34,6 +37,18 @@ const BasicLayout: React.FC<BasicLayoutProps> = ({ children }) => {
         wifiStrength: 0,
         heatCapacity: 0
     };
+    //使用key state更新整个basic layout ,用于加载新的内容组件
+    const [currentKey, setCurrentKey] = useState('home');
+    // key和内容组件的映射
+    const contentMap = {
+        'sub2': <AboutPage />,
+        'process': <HomePage />,
+        'register':<RegisterPage />
+    };
+    //感知菜单项点击
+    const handleMenuClick=(key:string)=>{
+        setCurrentKey(key)
+    };
     return (
         // ConfigProvider 用于自定义 Ant Design 的主题和 breakpoints
         <ConfigProvider
@@ -71,11 +86,12 @@ const BasicLayout: React.FC<BasicLayoutProps> = ({ children }) => {
                 <Row style={{ flex: 1, display: 'flex', height: '100%', border: '1px solid red' }}>
                     {/* 导航区域 */}
                     <Col xl={4} lg={4} md={0} sm={0} xs={0} style={{ flex: 1 }}>
-                        <NavMenu></NavMenu>
+                        <NavMenu onMenuClick={handleMenuClick} />
                     </Col>
                     {/* 内容区域 */}
                     <Col xl={20} lg={20} xs={24} md={24} sm={24} style={{ flex: 1, border: '1px solid blue' }}>
-                        {children}
+                        {/* {children} */}
+                        {contentMap[currentKey]}
                     </Col>
                 </Row>
                 {/* Tabbar:固定在底部,仅在手机屏幕显示 */}

+ 8 - 2
src/layouts/NavMenu.tsx

@@ -58,16 +58,22 @@ const items: MenuItem[] = [
     },
 ];
 
-const NavMenu: React.FC = () => {
+interface NavMenuProps {
+    onMenuClick?: (key: string) => void;
+}
+
+const NavMenu: React.FC<NavMenuProps> = ({ onMenuClick }) => {
     const onClick: MenuProps['onClick'] = (e) => {
         console.log('click ', e);
+        onMenuClick?.(e.key);
     };
+
     // 从items得到所有key
     const allKeys = items.map(item => item?.key as string);
     const [openKeys, setOpenKeys] = useState(allKeys); // 初始展开的 key
 
     // 阻止默认的折叠行为
-    const onOpenChange = (keys) => {
+    const onOpenChange = (keys: string[]) => {
         // 如果当前的 key 在 openKeys 中,说明用户试图关闭它,我们忽略这个操作
         if (openKeys.includes(keys[0])) {
             return;

+ 79 - 0
src/pages/patient/components/register.form.tsx

@@ -0,0 +1,79 @@
+import React from 'react';
+import { Form, Input, DatePicker, InputNumber, Select, Radio } from 'antd';
+
+const genderOptions = [
+  { value: 'male', label: '男' },
+  { value: 'female', label: '女' },
+];
+
+const bodyTypeOptions = [
+  { value: 'slim', label: '瘦' },
+  { value: 'average', label: '平均' },
+  {value:"fat",label:'重'}
+];
+
+const pregnancyStatusOptions = [
+  { value: 'yes', label: '是' },
+  { value: 'no', label: '否' },
+  { value: 'na', label: '不适用' },
+];
+
+const bodyPartOptions = [
+  { value: 'head', label: '头部' },
+  { value: 'chest', label: '胸部' },
+];
+interface BasicInfoFormProps {
+    style?: React.CSSProperties;
+}
+const BasicInfoForm: React.FC<BasicInfoFormProps> = ({style}) => (
+  <Form layout="vertical" style={style}>
+    <Form.Item label="患者编号" name="patientId">
+      <Input placeholder="请输入患者编号" />
+    </Form.Item>
+    <Form.Item label="患者姓名" name="patientName">
+      <Input placeholder="请输入患者姓名" />
+    </Form.Item>
+    <Form.Item label="曾用名" name="previousName">
+      <Input placeholder="请输入曾用名" />
+    </Form.Item>
+    <Form.Item label="英文名" name="englishName">
+      <Input placeholder="请输入英文名" />
+    </Form.Item>
+    <Form.Item label="登记号" name="registrationNo">
+      <Input placeholder="请输入登记号" />
+    </Form.Item>
+    <Form.Item label="出生日期" name="dateOfBirth">
+      <DatePicker format="YYYY-MM-DD" style={{ width: '100%' }} />
+    </Form.Item>
+    <Form.Item label="年龄" name="age">
+      <InputNumber min={0} style={{ width: '100%' }} />
+    </Form.Item>
+    <Form.Item label="性别" name="gender">
+      <Select options={genderOptions} />
+    </Form.Item>
+    <Form.Item label="病人体型" name="bodyType">
+      <Select options={bodyTypeOptions} />
+    </Form.Item>
+    <Form.Item label="体重" name="weight">
+      <InputNumber min={0} addonAfter="kg" style={{ width: '100%' }} />
+    </Form.Item>
+    <Form.Item label="身高" name="height">
+      <InputNumber min={0} addonAfter="cm" style={{ width: '100%' }} />
+    </Form.Item>
+    <Form.Item label="怀孕状态" name="pregnancyStatus">
+      <Radio.Group
+        options={pregnancyStatusOptions}
+        optionType="button"
+        buttonStyle="solid"
+      />
+    </Form.Item>
+    <Form.Item label="转诊医师" name="referringPhysician">
+      <Input placeholder="请输入转诊医师姓名" />
+    </Form.Item>
+    <Form.Item label="身体部位" name="bodyPart">
+      <Select options={bodyPartOptions} />
+    </Form.Item>
+  </Form>
+);
+
+export default BasicInfoForm;

+ 55 - 0
src/pages/patient/components/register.protocol.list.tsx

@@ -0,0 +1,55 @@
+import React from 'react';
+import { Card, Grid } from 'antd';
+
+const { useBreakpoint } = Grid;
+
+// 示例协议数据
+const protocols = [
+  { id: 1, name: '协议A' },
+  { id: 2, name: '协议B' },
+  { id: 3, name: '协议C' },
+  { id: 4, name: '协议D' },
+  { id: 5, name: '协议E' },
+  { id: 6, name: '协议F' },
+];
+
+// 卡片尺寸配置
+const getCardSize = (screens: ReturnType<typeof useBreakpoint>) => {
+  if (screens.xl || screens.xxl) {
+    return { width: 150, height: 100 };
+  }
+  if (screens.md || screens.lg) {
+    return { width: 90, height: 60 };
+  }
+  return { width: 60, height: 45 };
+};
+
+const ProtocolSelectionList: React.FC = () => {
+  const screens = useBreakpoint();
+  const cardSize = getCardSize(screens);
+
+  return (
+    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 16 }}>
+      {protocols.map((item) => (
+        <Card
+          key={item.id}
+          hoverable
+          style={{
+            width: cardSize.width,
+            height: cardSize.height,
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'center',
+            marginBottom: 16,
+            marginRight: 16,
+          }}
+          bodyStyle={{ padding: 0, textAlign: 'center' }}
+        >
+          {item.name}
+        </Card>
+      ))}
+    </div>
+  );
+};
+
+export default ProtocolSelectionList;

+ 87 - 0
src/pages/patient/components/register.selected.protocol.list.tsx

@@ -0,0 +1,87 @@
+import React from 'react';
+import { Card, Grid, Button, Flex } from 'antd';
+import { CloseOutlined } from '@ant-design/icons';
+
+const { useBreakpoint } = Grid;
+
+// 示例已选协议数据
+const selectedProtocols = [
+  { id: 1, name: '已选协议A' },
+  { id: 2, name: '已选协议B' },
+  { id: 3, name: '已选协议C' },
+];
+
+// 卡片尺寸配置
+const getCardSize = (screens: ReturnType<typeof useBreakpoint>) => {
+  if (screens.xl || screens.xxl) {
+    return { width: 150, height: 100 };
+  }
+  if (screens.md || screens.lg) {
+    return { width: 90, height: 60 };
+  }
+  return { width: 60, height: 45 };
+};
+interface SelectedProtocolListProps {
+    style?: React.CSSProperties;
+    className?: string;
+}
+const SelectedProtocolList: React.FC<SelectedProtocolListProps> = () => {
+  const screens = useBreakpoint();
+  const cardSize = getCardSize(screens);
+
+  // 移除操作(实际项目可用props传递onRemove)
+  const handleRemove = (id: number) => {
+    // 这里仅做演示
+    // 实际应通过props回调或状态管理移除
+    alert(`移除协议ID: ${id}`);
+  };
+// { display: 'flex', flexWrap: 'wrap', gap: 16 }
+  return (
+    <Flex>
+      {selectedProtocols.map((item) => (
+        <Card
+          key={item.id}
+          hoverable
+          style={{
+            width: cardSize.width,
+            height: cardSize.height,
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'center',
+            position: 'relative',
+            marginBottom: 16,
+            marginRight: 16,
+          }}
+        >
+          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }} className='overflow-clip'>
+            <img 
+              alt="example" 
+              src="https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png"
+              style={{
+            width: cardSize.width * 0.6,
+            height: cardSize.height * 0.5,
+            objectFit: 'cover'
+              }}
+            />
+            <span style={{ marginTop: 4 }}>{item.name}</span>
+          </div>
+          <Button
+            type="text"
+            size="small"
+            icon={<CloseOutlined />}
+            onClick={() => handleRemove(item.id)}
+            style={{
+              position: 'absolute',
+              top: 2,
+              right: 2,
+              color: '#999',
+            }}
+            aria-label="移除"
+          />
+        </Card>
+      ))}
+    </Flex>
+  );
+};
+
+export default SelectedProtocolList;

+ 75 - 0
src/pages/patient/register.tsx

@@ -0,0 +1,75 @@
+import React from 'react';
+import { Row, Col, Card, Collapse, Grid, Divider } from 'antd';
+import BasicInfoForm from './components/register.form';
+import ProtocolList from './components/register.protocol.list';
+import SelectedProtocolList from './components/register.selected.protocol.list';
+
+const { useBreakpoint } = Grid;
+const { Panel } = Collapse;
+
+
+const RegisterPage: React.FC = () => {
+    const screens = useBreakpoint();
+
+    // xs: <480, sm: ≥576, md: ≥768, lg: ≥992, xl: ≥1200, xxl: ≥1600
+    // 优先级:xs/sm(小屏)→ md/lg(中屏)→ xl/xxl(大屏)
+    console.log(screens);
+
+    if (screens.xl || screens.xxl) {
+        // 大屏幕:三栏布局
+        return (
+            <Row  className='flex-1'>
+                <Col xs={24} sm={24} md={8} lg={8} xl={8} xxl={8}  className='flex-1 border border-green-500 h-8'>
+                    <BasicInfoForm style={{ flex: 1 }} />
+                </Col>
+                <Col xs={24} sm={24} md={8} lg={8} xl={8} xxl={8}>
+                    <ProtocolList />
+                </Col>
+                <Col xs={24} sm={24} md={8} lg={8} xl={8} xxl={8}>
+                    <SelectedProtocolList />
+                </Col>
+            </Row>
+        );
+    }
+
+    if (screens.md || screens.lg) {
+        // 中屏幕:左右两栏,右侧上下分
+        return (
+            <Row gutter={16} style={{ margin: 16 }}>
+                <Col xs={24} sm={24} md={12} lg={12}>
+                    <BasicInfoForm />
+                </Col>
+                <Col xs={24} sm={24} md={12} lg={12}>
+                    <Row gutter={[0, 16]}>
+                        <Col span={24}>
+                            <ProtocolList />
+                        </Col>
+                        <Divider />
+                        <Col span={24}>
+                            <SelectedProtocolList className="" />
+                        </Col>
+                    </Row>
+                </Col>
+            </Row>
+        );
+    }
+
+    if (screens.xs || screens.sm) {
+        // 小屏幕:手风琴式布局
+        return (
+            <Collapse accordion defaultActiveKey={['1']} style={{ margin: 16 }}>
+                <Panel header="基本信息表单区域" key="1">
+                    <BasicInfoForm />
+                </Panel>
+                <Panel header="待选择协议列表区域" key="2">
+                    <ProtocolList />
+                </Panel>
+                <Panel header="已选择协议列表区域" key="3">
+                    <SelectedProtocolList />
+                </Panel>
+            </Collapse>
+        );
+    }
+};
+
+export default RegisterPage;