123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- import React, { useEffect } from 'react';
- import { Layout, Button, Checkbox, Typography, List, Spin, Alert, message } from 'antd';
- import { ArrowLeftOutlined, ReloadOutlined } from '@ant-design/icons';
- import { useDispatch, useSelector } from 'react-redux';
- import { switchToOperationPanel } from '@/states/patient/worklist/slices/historyPanelSwitchSlice';
- import {
- fetchPacsNodesThunk,
- toggleNodeSelection,
- selectAllNodes,
- deselectAllNodes,
- } from '@/states/output/pacsNode/pacsNodeSlice';
- import { RootState, AppDispatch } from '@/states/store';
- import Icon from '@/components/Icon';
- const { Header, Content, Footer } = Layout;
- const { Title } = Typography;
- const SendImagePage = () => {
- const dispatch = useDispatch<AppDispatch>();
- // 从Redux获取PACS节点状态
- const { nodes, loading, error, selectedNodeIds } = useSelector(
- (state: RootState) => state.pacsNode
- );
- // 组件挂载时获取PACS节点列表
- useEffect(() => {
- dispatch(fetchPacsNodesThunk());
- }, [dispatch]);
- // 返回操作面板
- const handleReturn = () => {
- dispatch(switchToOperationPanel());
- };
- // 切换节点选中状态
- const handleNodeToggle = (nodeId: number) => {
- dispatch(toggleNodeSelection(nodeId));
- };
- // 全选/取消全选
- const handleSelectAll = (checked: boolean) => {
- if (checked) {
- dispatch(selectAllNodes());
- } else {
- dispatch(deselectAllNodes());
- }
- };
- // 重新加载节点列表
- const handleReload = () => {
- dispatch(fetchPacsNodesThunk());
- };
- // 发送图像
- const handleSend = () => {
- if (selectedNodeIds.length === 0) {
- message.warning('请至少选择一个PACS节点');
- return;
- }
-
- // TODO: 实现实际的发送图像功能
- message.info(`准备发送到 ${selectedNodeIds.length} 个节点`);
- console.log('Selected node IDs:', selectedNodeIds);
- };
- // 是否全选
- const isAllSelected = nodes.length > 0 &&
- nodes.filter(node => node.is_enabled).every(node =>
- selectedNodeIds.includes(node.id)
- );
-
- // 是否部分选中
- const isIndeterminate = selectedNodeIds.length > 0 && !isAllSelected;
- return (
- <Layout className="h-full">
- {/* 顶部导航栏 */}
- <Header
- style={{
- display: 'flex',
- alignItems: 'center',
- padding: '0 16px',
- justifyContent: 'space-between',
- }}
- >
- <div style={{ display: 'flex', alignItems: 'center' }}>
- <Button
- type="text"
- icon={<ArrowLeftOutlined />}
- onClick={handleReturn}
- />
- <Title level={5} style={{ margin: 0, lineHeight: '48px' }}>
- 发送图像
- </Title>
- </div>
- <Button
- type="text"
- icon={<ReloadOutlined />}
- onClick={handleReload}
- loading={loading}
- />
- </Header>
- {/* 主体内容 */}
- <Content
- style={{ padding: '16px', maxHeight: '100%', overflowY: 'auto' }}
- >
- {/* 错误提示 */}
- {error && (
- <Alert
- message="加载失败"
- description={error}
- type="error"
- showIcon
- closable
- style={{ marginBottom: 16 }}
- action={
- <Button size="small" onClick={handleReload}>
- 重试
- </Button>
- }
- />
- )}
- {/* 加载状态 */}
- {loading && (
- <div style={{ textAlign: 'center', padding: '40px 0' }}>
- <Spin size="large" tip="正在加载PACS节点..." />
- </div>
- )}
- {/* 节点列表 */}
- {!loading && !error && (
- <>
- {nodes.length === 0 ? (
- <Alert
- message="暂无PACS节点"
- description="请先在系统管理中配置PACS节点"
- type="info"
- showIcon
- />
- ) : (
- <>
- {/* 全选复选框 */}
- <div style={{ marginBottom: 16, borderBottom: '1px solid #f0f0f0', paddingBottom: 8 }}>
- <Checkbox
- indeterminate={isIndeterminate}
- checked={isAllSelected}
- onChange={(e) => handleSelectAll(e.target.checked)}
- >
- 全选 ({selectedNodeIds.length}/{nodes.filter(n => n.is_enabled).length})
- </Checkbox>
- </div>
- {/* PACS节点列表 */}
- <List
- dataSource={nodes}
- renderItem={(node) => (
- <List.Item>
- <Checkbox
- checked={selectedNodeIds.includes(node.id)}
- onChange={() => handleNodeToggle(node.id)}
- disabled={!node.is_enabled}
- >
- <div>
- <div style={{ fontWeight: 500 }}>
- {node.name}
- {node.is_default && (
- <span style={{ marginLeft: 8, color: '#1890ff', fontSize: '12px' }}>
- [默认]
- </span>
- )}
- {!node.is_enabled && (
- <span style={{ marginLeft: 8, color: '#999', fontSize: '12px' }}>
- [已禁用]
- </span>
- )}
- </div>
- <div style={{ fontSize: '12px', color: '#666', marginTop: 4 }}>
- {node.address}:{node.port} | AET: {node.aet}
- </div>
- </div>
- </Checkbox>
- </List.Item>
- )}
- />
- </>
- )}
- </>
- )}
- </Content>
- {/* 底部按钮 */}
- <Footer
- style={{
- padding: '16px',
- display: 'flex',
- justifyContent: 'center',
- }}
- >
- <Button
- type="primary"
- icon={
- <Icon
- module="module-output"
- name="Send"
- userId="base"
- theme="default"
- size="2x"
- state="normal"
- />
- }
- style={{ width: '100%', maxWidth: '400px' }}
- onClick={handleSend}
- disabled={selectedNodeIds.length === 0 || loading}
- >
- 发送图像 ({selectedNodeIds.length})
- </Button>
- </Footer>
- </Layout>
- );
- };
- export default SendImagePage;
|