站点信息页面用于配置和管理医学影像系统的 DICOM 站点基本信息,包括站点名称、AE Title、端口、机构信息等核心参数。
Page (页面)
└── Main Container (主容器)
├── Header Section (头部区域)
│ └── Title Component (标题组件)
│ └── Typography.Title (H3)
│
├── Form Section (表单区域)
│ └── Form Container (表单容器)
│ ├── Form.Item (站点名称)
│ │ ├── Label (标签)
│ │ └── Input (输入框)
│ │
│ ├── Form.Item (AE Title)
│ │ ├── Label
│ │ └── Input
│ │
│ ├── Form.Item (端口)
│ │ ├── Label
│ │ └── InputNumber
│ │
│ ├── Form.Item (机构名称)
│ │ ├── Label
│ │ └── Input
│ │
│ ├── Form.Item (站点编码)
│ │ ├── Label
│ │ └── Input
│ │
│ └── Form.Item (描述)
│ ├── Label
│ └── TextArea
│
└── Footer Section (底部区域)
└── Button Group (按钮组)
├── Button (保存)
└── Button (重置)
| 组件 | 用途 | 选择理由 |
|---|---|---|
| Typography.Title | 页面标题 | - 提供标准的标题样式 - 自动适配主题 - 支持多级标题 |
| Form | 表单容器 | - 提供完整的表单验证 - 自动处理表单布局 - 内置错误显示 - 支持受控/非受控模式 |
| Form.Item | 表单项包装器 | - 统一标签和输入框布局 - 提供验证反馈UI - 自动处理错误信息显示 |
| Input | 文本输入 | - 标准文本输入组件 - 支持前缀/后缀图标 - 提供清空功能 - 自动trim空格 |
| InputNumber | 数字输入 | - 专门用于数字输入 - 内置数字验证 - 支持步进器 - 可限制最大最小值 |
| Input.TextArea | 多行文本 | - 适合长文本输入 - 支持自动调整高度 - 可限制最大长度 - 显示字符计数 |
| Button | 操作按钮 | - 提供多种按钮类型 - 支持加载状态 - 自动防抖保护 - 主题一致性 |
| Space | 间距容器 | - 统一管理组件间距 - 响应式间距 - 自动换行支持 |
选择 Horizontal Layout(水平布局) 的原因:
[x] 数据展示
[x] 数据编辑
[x] 数据验证
[x] 数据保存
[x] 表单操作
需求描述:
交互流程:
验证规则:
边界情况:
需求描述:
交互流程:
验证规则:
边界情况:
需求描述:
交互流程:
验证规则:
边界情况:
需求描述:
交互流程:
验证规则:
边界情况:
需求描述:
交互流程:
边界情况:
// states/siteInfoSlice.ts
interface SiteInfoState {
// 站点信息数据
data: {
siteName: string;
aeTitle: string;
port: number;
institutionName: string;
siteCode: string;
description: string;
} | null;
// 加载状态
loading: boolean;
// 保存状态
saving: boolean;
// 错误信息
error: string | null;
// 是否有未保存的修改
isDirty: boolean;
}
// 异步 Thunks
export const fetchSiteInfo = createAsyncThunk(...);
export const saveSiteInfo = createAsyncThunk(...);
// API/siteInfo.ts
// 获取站点信息
export const getSiteInfo = (): Promise<SiteInfoData> => {
return request.get('/api/system/site-info');
};
// 保存站点信息
export const updateSiteInfo = (data: SiteInfoData): Promise<void> => {
return request.put('/api/system/site-info', data);
};
// 验证 AE Title 唯一性(可选)
export const validateAETitle = (aeTitle: string): Promise<boolean> => {
return request.post('/api/system/site-info/validate-ae-title', { aeTitle });
};
// 检查端口可用性(可选)
export const checkPortAvailability = (port: number): Promise<boolean> => {
return request.post('/api/system/site-info/check-port', { port });
};
// validation/siteInfoRules.ts
import { z } from 'zod';
export const siteInfoSchema = z.object({
siteName: z.string()
.min(1, '站点名称不能为空')
.max(50, '站点名称最多50个字符'),
aeTitle: z.string()
.min(1, 'AE Title 不能为空')
.max(16, 'AE Title 最多16个字符')
.regex(/^[A-Z0-9_]+$/, 'AE Title 只能包含大写字母、数字和下划线'),
port: z.number()
.int('端口必须是整数')
.min(1, '端口号最小为1')
.max(65535, '端口号最大为65535'),
institutionName: z.string()
.max(100, '机构名称最多100个字符')
.optional(),
siteCode: z.string()
.max(20, '站点编码最多20个字符')
.optional(),
description: z.string()
.max(500, '描述最多500个字符')
.optional(),
});
// sections/SystemHome/SiteInfo.tsx
import React, { useEffect } from 'react';
import { Form, Input, InputNumber, Button, message, Spin } from 'antd';
import { useAppDispatch, useAppSelector } from '@/states/store';
import { fetchSiteInfo, saveSiteInfo } from '@/states/siteInfoSlice';
const SiteInfo: React.FC = () => {
const [form] = Form.useForm();
const dispatch = useAppDispatch();
const { data, loading, saving } = useAppSelector(state => state.siteInfo);
// 加载数据
useEffect(() => {
dispatch(fetchSiteInfo());
}, [dispatch]);
// 数据加载后填充表单
useEffect(() => {
if (data) {
form.setFieldsValue(data);
}
}, [data, form]);
// 保存处理
const handleSave = async () => {
try {
const values = await form.validateFields();
await dispatch(saveSiteInfo(values)).unwrap();
message.success('保存成功');
} catch (error) {
message.error('保存失败');
}
};
// 重置处理
const handleReset = () => {
form.resetFields();
};
if (loading) {
return <Spin />;
}
return (
<div style={{ padding: 24 }}>
<Typography.Title level={3}>站点信息</Typography.Title>
<Form
form={form}
layout="horizontal"
labelCol={{ span: 6 }}
wrapperCol={{ span: 14 }}
>
{/* 表单项... */}
<Form.Item wrapperCol={{ offset: 6 }}>
<Space>
<Button type="primary" onClick={handleSave} loading={saving}>
保存
</Button>
<Button onClick={handleReset}>重置</Button>
</Space>
</Form.Item>
</Form>
</div>
);
};
export default SiteInfo;
用户打开页面
↓
加载站点信息 (fetchSiteInfo)
↓
显示加载状态
↓
数据加载成功 → 填充表单
↓
用户编辑表单
↓
实时验证 (可选)
↓
点击保存按钮
↓
前端验证
↓
验证失败 → 显示错误
↓
验证成功 → 提交到后端
↓
显示保存状态
↓
保存成功 → 显示成功消息
↓
保存失败 → 显示错误 + 重试选项
// assets/i18n/zh-CN.json
{
"systemSettings": {
"siteInfo": {
"title": "站点信息",
"siteName": "站点名称",
"aeTitle": "AE Title",
"port": "端口",
"institutionName": "机构名称",
"siteCode": "站点编码",
"description": "描述",
"save": "保存",
"reset": "重置",
"saveSuccess": "保存成功",
"saveFailed": "保存失败"
}
}
}