837390164@qq.com преди 13 часа
родител
ревизия
8e0d27d3eb
променени са 4 файла, в които са добавени 672 реда и са изтрити 20 реда
  1. 1 1
      src/App.tsx
  2. 74 0
      src/api/basicData.ts
  3. 597 19
      src/pages/BasicData/DIPCoreAlgorithmConfig.tsx
  4. 0 0
      src/pages/BasicData/DRGCoreAlgorithmConfig.tsx

+ 1 - 1
src/App.tsx

@@ -41,7 +41,7 @@ import SelectHospRole from './pages/Login/SelectHospRole';
 import ICDMapping from './pages/BasicData/ICDMapping';
 import ICDQuery from './pages/BasicData/ICDQuery';
 import ADRGRuleMaintenance from './pages/BasicData/ADRGRuleMaintenance';
-import CoreAlgorithmConfig from './pages/BasicData/CoreAlgorithmConfig';
+import CoreAlgorithmConfig from './pages/BasicData/DRGCoreAlgorithmConfig';
 import BasicDataMaintenance from './pages/BasicData/BasicDataMaintenance';
 import DIPDisease from './pages/BasicData/DIPDisease';
 import TableDataMaintenance from './pages/BasicData/TableDataMaintenance';

+ 74 - 0
src/api/basicData.ts

@@ -1073,3 +1073,77 @@ export const deleteDipCoreAlgorithm = (
 ): Promise<ApiResponse> => {
   return invoke('02010055', [{ id }]);
 };
+
+// ========== DIP核心算法配置导入 (02010056, 02010057, 02010058) ==========
+
+/** DIP核心算法导入预览数据项 */
+export interface DipCoreAlgorithmImportPreviewItem {
+  rowNum: number;
+  principalDiagnosis: string;
+  principalDiagnosisName: string;
+  majorProcedure: string;
+  majorProcedureName: string;
+  secondaryProcedure: string;
+  secondaryProcedureName: string;
+  scoreValue: string;
+  adjustCoefficient: string;
+  primaryAdjustCoefficient: string;
+  secondaryAdjustCoefficient: string;
+  thirdAdjustCoefficient: string;
+  status: 'valid' | 'duplicate' | 'invalid';
+  statusDesc: string;
+  errorMsg?: string;
+}
+
+/** DIP核心算法导入预览结果 */
+export interface DipCoreAlgorithmImportPreviewResult {
+  totalCount: number;
+  validCount: number;
+  invalidCount: number;
+  duplicateCount: number;
+  previewList: DipCoreAlgorithmImportPreviewItem[];
+}
+
+/** DIP核心算法导入结果 */
+export interface DipCoreAlgorithmImportResult {
+  totalCount: number;
+  successCount: number;
+  failCount: number;
+  duplicateCount: number;
+  newCount: number;
+  failList: Array<{
+    rowNum: number;
+    principalDiagnosis: string;
+    errorMsg: string;
+  }>;
+  reportUrl?: string;
+}
+
+/** DIP核心算法导入参数 */
+export interface DipCoreAlgorithmImportParams {
+  provinceID: string;
+  cityID: string;
+  mdtrtArea: string;
+  medinsLv: string;
+  fileData: string;
+  fileName: string;
+}
+
+/** 下载DIP核心算法配置导入模板 (02010056) */
+export const downloadDipCoreAlgorithmTemplate = (): Promise<ApiResponse<{ fileName: string; fileData: string; contentType: string }>> => {
+  return invoke('02010056', []);
+};
+
+/** DIP核心算法配置导入预览 (02010057) */
+export const previewDipCoreAlgorithmImport = (
+  params: DipCoreAlgorithmImportParams
+): Promise<ApiResponse<DipCoreAlgorithmImportPreviewResult>> => {
+  return invoke('02010057', [params]);
+};
+
+/** DIP核心算法配置确认导入 (02010058) */
+export const confirmDipCoreAlgorithmImport = (
+  params: DipCoreAlgorithmImportParams
+): Promise<ApiResponse<DipCoreAlgorithmImportResult>> => {
+  return invoke('02010058', [params]);
+};

+ 597 - 19
src/pages/BasicData/DIPCoreAlgorithmConfig.tsx

@@ -1,21 +1,31 @@
 /**
  * DIP核心算法配置页面
- * 功能:列表查询、新增/编辑、删除
+ * 功能:列表查询、新增/编辑、删除、导入
  */
 
-import React, { useState, useEffect, useCallback } from 'react';
+import React, { useState, useEffect, useCallback, useRef } from 'react';
 import {
-  Table, Button, Card, Space, message, Popconfirm, Form, Select, Input, Row, Col, Modal, InputNumber
+  Table, Button, Card, Space, message, Popconfirm, Form, Select, Input, Row, Col, Modal, InputNumber,
+  Upload, Steps, Alert, Statistic, Divider, Typography, List, Result, Tag
 } from 'antd';
 import {
-  PlusOutlined, SearchOutlined, ReloadOutlined
+  PlusOutlined, SearchOutlined, ReloadOutlined, UploadOutlined, DownloadOutlined, EyeOutlined,
+  CheckCircleOutlined, CloseCircleOutlined, WarningOutlined, FileExcelOutlined, ArrowRightOutlined, ArrowLeftOutlined
 } from '@ant-design/icons';
+import type { UploadFile } from 'antd/es/upload/interface';
+import type { ColumnsType } from 'antd/es/table';
 import CustomPagination from '../../components/CustomPagination';
 import {
   queryDipCoreAlgorithm, deleteDipCoreAlgorithm,
   saveDipCoreAlgorithm,
   getProvinceData, getCityData,
+  downloadDipCoreAlgorithmTemplate,
+  previewDipCoreAlgorithmImport,
+  confirmDipCoreAlgorithmImport,
   type DipCoreAlgorithmItem,
+  type DipCoreAlgorithmImportPreviewItem,
+  type DipCoreAlgorithmImportResult,
+  type DipCoreAlgorithmImportParams,
   type ProvinceItem, type CityItem
 } from '../../api/basicData';
 
@@ -55,6 +65,22 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
   const [modalProvinceLoading, setModalProvinceLoading] = useState(false);
   const [modalCityLoading, setModalCityLoading] = useState(false);
 
+  // ==================== 导入功能状态 ====================
+  const [importModalVisible, setImportModalVisible] = useState(false);
+  const [importStep, setImportStep] = useState(0);
+  const [importLoading, setImportLoading] = useState(false);
+  const [importForm] = Form.useForm();
+  const [importProvinceList, setImportProvinceList] = useState<ProvinceItem[]>([]);
+  const [importCityList, setImportCityList] = useState<CityItem[]>([]);
+  const [importProvinceLoading, setImportProvinceLoading] = useState(false);
+  const [importCityLoading, setImportCityLoading] = useState(false);
+  const [fileList, setFileList] = useState<UploadFile[]>([]);
+  const [previewData, setPreviewData] = useState<DipCoreAlgorithmImportPreviewItem[]>([]);
+  const [previewStats, setPreviewStats] = useState({ total: 0, valid: 0, invalid: 0, duplicate: 0 });
+  const [importResult, setImportResult] = useState<DipCoreAlgorithmImportResult | null>(null);
+  const fileContentRef = useRef<string>('');
+  const fileNameRef = useRef<string>('');
+
   // 加载省数据(查询)
   const loadProvinces = useCallback(async () => {
     setProvinceLoading(true);
@@ -307,6 +333,237 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
     setModalVisible(false);
   };
 
+  // ========== 导入功能方法 ====================
+
+  // 打开导入弹窗
+  const handleOpenImport = async () => {
+    setImportModalVisible(true);
+    setImportStep(0);
+    setFileList([]);
+    setPreviewData([]);
+    setImportResult(null);
+    importForm.resetFields();
+    fileContentRef.current = '';
+    fileNameRef.current = '';
+    setImportCityList([]);
+
+    setImportProvinceLoading(true);
+    try {
+      const res = await getProvinceData();
+      if (res.errorCode === '0' && res.result) {
+        setImportProvinceList(res.result);
+      }
+    } finally {
+      setImportProvinceLoading(false);
+    }
+  };
+
+  // 导入弹窗省选择变化
+  const handleImportProvinceChange = (value: string) => {
+    importForm.setFieldsValue({
+      importProvinceId: value,
+      importCityId: undefined,
+    });
+    setImportCityList([]);
+
+    if (!value) {
+      return;
+    }
+    setImportCityLoading(true);
+    getCityData(value).then(res => {
+      if (res.errorCode === '0' && res.result) {
+        setImportCityList(res.result);
+      }
+      setImportCityLoading(false);
+    });
+  };
+
+  // 导入弹窗市选择变化
+  const handleImportCityChange = (value: string) => {
+    importForm.setFieldsValue({
+      importCityId: value,
+    });
+  };
+
+  // 下载导入模板
+  const handleDownloadTemplate = async () => {
+    try {
+      const res = await downloadDipCoreAlgorithmTemplate();
+      if (res.errorCode === '0' && res.result) {
+        const byteString = atob(res.result.fileData);
+        const ab = new ArrayBuffer(byteString.length);
+        const ia = new Uint8Array(ab);
+        for (let i = 0; i < byteString.length; i++) {
+          ia[i] = byteString.charCodeAt(i);
+        }
+        const blob = new Blob([ab], { type: res.result.contentType || 'text/csv' });
+        const url = URL.createObjectURL(blob);
+        const link = document.createElement('a');
+        link.href = url;
+        link.download = res.result.fileName;
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+        URL.revokeObjectURL(url);
+        message.success('模板下载成功');
+      } else {
+        message.error(res.errorMessage || '下载模板失败');
+      }
+    } catch (error: any) {
+      message.error('下载模板失败:' + (error.message || '网络异常'));
+    }
+  };
+
+  // 文件上传前处理
+  const beforeUpload = (file: UploadFile) => {
+    const rawFile = file.originFileObj || file;
+    const isExcel = rawFile.type === 'application/vnd.open-excel' ||
+                    rawFile.type === 'application/vnd.ms-excel' ||
+                    rawFile.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
+                    rawFile.name?.endsWith('.csv');
+    if (!isExcel) {
+      message.error('请上传Excel或CSV文件!');
+      return false;
+    }
+    const isLt10M = (rawFile.size || 0) / 1024 / 1024 < 10;
+    if (!isLt10M) {
+      message.error('文件大小不能超过10MB!');
+      return false;
+    }
+
+    const reader = new FileReader();
+    reader.onload = (e) => {
+      const arrayBuffer = e.target?.result as ArrayBuffer;
+      const bytes = new Uint8Array(arrayBuffer);
+      let binary = '';
+      for (let i = 0; i < bytes.byteLength; i++) {
+        binary += String.fromCharCode(bytes[i]);
+      }
+      const base64Content = btoa(binary);
+      fileContentRef.current = base64Content;
+      fileNameRef.current = rawFile.name || '';
+    };
+    reader.readAsArrayBuffer(rawFile as Blob);
+
+    setFileList([file]);
+    return false;
+  };
+
+  // 预览导入数据
+  const handlePreviewImport = async () => {
+    const provinceId = importForm.getFieldValue('importProvinceId');
+    const cityId = importForm.getFieldValue('importCityId');
+    const medinsLv = importForm.getFieldValue('importMedinsLv');
+
+    if (!provinceId) {
+      message.error('请先选择省');
+      return;
+    }
+    if (!cityId) {
+      message.error('请先选择市');
+      return;
+    }
+    if (!medinsLv) {
+      message.error('请先选择机构等级');
+      return;
+    }
+    if (fileList.length === 0) {
+      message.error('请先上传导入文件');
+      return;
+    }
+
+    const selectedCity = importCityList.find(c => c.id === cityId);
+
+    const params: DipCoreAlgorithmImportParams = {
+      provinceID: provinceId,
+      cityID: cityId,
+      mdtrtArea: selectedCity?.code || '',
+      medinsLv: medinsLv,
+      fileData: fileContentRef.current,
+      fileName: fileNameRef.current
+    };
+
+    try {
+      setImportLoading(true);
+      const res = await previewDipCoreAlgorithmImport(params);
+
+      if (res.errorCode === '0' && res.result) {
+        setPreviewData(res.result.previewList || []);
+        setPreviewStats({
+          total: res.result.totalCount,
+          valid: res.result.validCount,
+          invalid: res.result.invalidCount,
+          duplicate: res.result.duplicateCount
+        });
+        setImportStep(1);
+      } else {
+        message.error(res.errorMessage || '预览失败');
+      }
+    } catch (error: any) {
+      message.error('预览失败:' + (error.message || '网络异常'));
+    } finally {
+      setImportLoading(false);
+    }
+  };
+
+  // 确认导入
+  const handleConfirmImport = async () => {
+    const provinceId = importForm.getFieldValue('importProvinceId');
+    const cityId = importForm.getFieldValue('importCityId');
+    const medinsLv = importForm.getFieldValue('importMedinsLv');
+
+    if (!provinceId) {
+      message.error('请先选择省');
+      return;
+    }
+    if (!cityId) {
+      message.error('请先选择市');
+      return;
+    }
+    if (!medinsLv) {
+      message.error('请先选择机构等级');
+      return;
+    }
+
+    const selectedCity = importCityList.find(c => c.id === cityId);
+
+    const params: DipCoreAlgorithmImportParams = {
+      provinceID: String(provinceId),
+      cityID: String(cityId),
+      mdtrtArea: selectedCity?.code || '',
+      medinsLv: medinsLv,
+      fileData: fileContentRef.current,
+      fileName: fileNameRef.current
+    };
+
+    try {
+      setImportLoading(true);
+      const res = await confirmDipCoreAlgorithmImport(params);
+
+      if (res.result) {
+        setImportResult(res.result);
+        setImportStep(2);
+        fetchData();
+      } else {
+        message.error(res.errorMessage || '导入失败');
+      }
+    } catch (error: any) {
+      message.error('导入失败:' + (error.message || '网络异常'));
+    } finally {
+      setImportLoading(false);
+    }
+  };
+
+  // 关闭导入弹窗
+  const handleCloseImport = () => {
+    setImportModalVisible(false);
+    setImportStep(0);
+    setFileList([]);
+    setPreviewData([]);
+    setImportResult(null);
+    importForm.resetFields();
+  };
+
   // 医疗机构等级选项
   const medinsLvOptions = [
     { label: '一级', value: '1' },
@@ -407,13 +664,44 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
     },
   ];
 
+  // 预览表格列定义
+  const previewColumns: ColumnsType<DipCoreAlgorithmImportPreviewItem> = [
+    { title: '行号', dataIndex: 'rowNum', width: 60 },
+    { title: '主要诊断代码', dataIndex: 'principalDiagnosis', width: 120 },
+    { title: '主要诊断名称', dataIndex: 'principalDiagnosisName', width: 150, ellipsis: true },
+    { title: '主要手术代码', dataIndex: 'majorProcedure', width: 120 },
+    { title: '主要手术名称', dataIndex: 'majorProcedureName', width: 150, ellipsis: true },
+    { title: '基准分值', dataIndex: 'scoreValue', width: 90, align: 'right' },
+    { title: '调节系数', dataIndex: 'adjustCoefficient', width: 90, align: 'right' },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      width: 80,
+      render: (status: string) => {
+        if (status === 'valid') {
+          return <Tag color="success" icon={<CheckCircleOutlined />}>正常</Tag>;
+        } else if (status === 'duplicate') {
+          return <Tag color="warning" icon={<WarningOutlined />}>重复</Tag>;
+        } else {
+          return <Tag color="error" icon={<CloseCircleOutlined />}>错误</Tag>;
+        }
+      }
+    },
+    {
+      title: '备注',
+      dataIndex: 'errorMsg',
+      ellipsis: true,
+      render: (text: string) => text || '-'
+    }
+  ];
+
   return (
-    <div style={{ padding: 12 }}>
+    <div style={{ padding: 10 }}>
       <Card>
         {/* 查询表单 */}
         <Form layout="vertical" style={{ marginBottom: 1 }}>
           <Row gutter={8}>
-            <Col span={4}>
+            <Col span={3}>
               <Form.Item label="主要诊断代码">
                 <Input
                   placeholder="请输入主要诊断代码"
@@ -422,7 +710,7 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
                 />
               </Form.Item>
             </Col>
-            <Col span={4}>
+            <Col span={3}>
               <Form.Item label="主要诊断名称">
                 <Input
                   placeholder="请输入主要诊断名称"
@@ -431,7 +719,7 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
                 />
               </Form.Item>
             </Col>
-            <Col span={4}>
+            <Col span={3}>
               <Form.Item label="主要手术代码">
                 <Input
                   placeholder="请输入主要手术代码"
@@ -440,7 +728,7 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
                 />
               </Form.Item>
             </Col>
-            <Col span={4}>
+            <Col span={3}>
               <Form.Item label="主要手术名称">
                 <Input
                   placeholder="请输入主要手术名称"
@@ -449,9 +737,7 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
                 />
               </Form.Item>
             </Col>
-          </Row>
-          <Row gutter={16}>
-            <Col span={4}>
+            <Col span={3}>
               <Form.Item label="省">
                 <Select
                   placeholder="请选择省"
@@ -467,7 +753,7 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
                 </Select>
               </Form.Item>
             </Col>
-            <Col span={4}>
+            <Col span={3}>
               <Form.Item label="市">
                 <Select
                   placeholder="请选择市"
@@ -484,7 +770,7 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
                 </Select>
               </Form.Item>
             </Col>
-            <Col span={4}>
+            <Col span={2}>
               <Form.Item label="机构等级">
                 <Select
                   placeholder="全部"
@@ -499,7 +785,7 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
                 </Select>
               </Form.Item>
             </Col>
-            <Col span={3}>
+             <Col span={4}>
               <Form.Item label=" " style={{ marginBottom: 0 }}>
                 <Space>
                   <Button type="primary" icon={<SearchOutlined />} onClick={handleSearch}>
@@ -515,11 +801,16 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
         </Form>
 
         {/* 操作按钮 */}
-        <Row style={{ marginBottom: 6 }}>
+        <Row style={{ marginBottom: 2 }}>
           <Col>
-            <Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
-              新增配置
-            </Button>
+            <Space>
+              <Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
+                新增配置
+              </Button>
+              <Button type="primary" icon={<UploadOutlined />} onClick={handleOpenImport}>
+                导入
+              </Button>
+            </Space>
           </Col>
         </Row>
 
@@ -703,6 +994,293 @@ const DIPCoreAlgorithmConfig: React.FC = () => {
           </div>
         </Form>
       </Modal>
+
+      {/* 导入弹窗 */}
+      <Modal
+        title="DIP核心算法配置导入"
+        open={importModalVisible}
+        onCancel={handleCloseImport}
+        width={850}
+        footer={null}
+        destroyOnClose
+      >
+        <Steps
+          current={importStep}
+          style={{ marginBottom: 24 }}
+          items={[
+            { title: '上传文件', icon: <UploadOutlined /> },
+            { title: '数据预览', icon: <EyeOutlined /> },
+            { title: '导入结果', icon: <CheckCircleOutlined /> }
+          ]}
+        />
+
+        {/* 步骤1:上传文件 */}
+        {importStep === 0 && (
+          <div>
+            <Alert
+              message="导入前请先选择省市"
+              description="导入的DIP核心算法配置将归属到选定的省市,请仔细确认。"
+              type="warning"
+              showIcon
+              style={{ marginBottom: 16 }}
+            />
+            <Form form={importForm} layout="vertical">
+              <Row gutter={16}>
+                <Col span={8}>
+                  <Form.Item
+                    name="importProvinceId"
+                    label="省"
+                    rules={[{ required: true, message: '请选择省' }]}
+                  >
+                    <Select
+                      placeholder="请选择省"
+                      loading={importProvinceLoading}
+                      showSearch
+                      optionFilterProp="children"
+                      onChange={handleImportProvinceChange}
+                    >
+                      {importProvinceList.map(item => (
+                        <Option key={item.id} value={item.id}>{item.descripts}</Option>
+                      ))}
+                    </Select>
+                  </Form.Item>
+                </Col>
+                <Col span={8}>
+                  <Form.Item
+                    name="importCityId"
+                    label="市"
+                    rules={[{ required: true, message: '请选择市' }]}
+                  >
+                    <Select
+                      placeholder="请选择市"
+                      loading={importCityLoading}
+                      showSearch
+                      optionFilterProp="children"
+                      onChange={handleImportCityChange}
+                      disabled={!importForm.getFieldValue('importProvinceId')}
+                    >
+                      {importCityList.map(item => (
+                        <Option key={item.id} value={item.id}>{item.descripts}</Option>
+                      ))}
+                    </Select>
+                  </Form.Item>
+                </Col>
+                <Col span={8}>
+                  <Form.Item
+                    name="importMedinsLv"
+                    label="机构等级"
+                    rules={[{ required: true, message: '请选择机构等级' }]}
+                  >
+                    <Select placeholder="请选择机构等级">
+                      <Option value="1">一级</Option>
+                      <Option value="2">二级</Option>
+                      <Option value="3">三级</Option>
+                    </Select>
+                  </Form.Item>
+                </Col>
+              </Row>
+
+              <Divider />
+
+              <Form.Item label="导入文件" required>
+                <Upload.Dragger
+                  fileList={fileList}
+                  beforeUpload={beforeUpload}
+                  onRemove={() => {
+                    setFileList([]);
+                    fileContentRef.current = '';
+                    fileNameRef.current = '';
+                  }}
+                  accept=".xlsx,.xls,.csv"
+                  maxCount={1}
+                >
+                  <p className="ant-upload-drag-icon">
+                    <FileExcelOutlined style={{ color: '#52c41a' }} />
+                  </p>
+                  <p className="ant-upload-text">点击或拖拽文件到此区域上传</p>
+                  <p className="ant-upload-hint">
+                    仅支持.csv 格式,文件大小不超过10MB
+                  </p>
+                </Upload.Dragger>
+              </Form.Item>
+
+              <div style={{ textAlign: 'center' }}>
+                <Button icon={<DownloadOutlined />} onClick={handleDownloadTemplate}>
+                  下载导入模板
+                </Button>
+              </div>
+
+              <Divider />
+
+              <Alert
+                message="导入说明"
+                description={
+                  <ul style={{ margin: 0, paddingLeft: 16 }}>
+                    <li>必填列:主要诊断代码(PrincipalDiagnosis)、主要诊断名称(PrincipalDiagnosisName)、基准分值(ScoreValue)</li>
+                    <li>导入设置:省、市、机构等级由上方选择决定</li>
+                    <li>可选列:主要手术代码/名称、相关手术代码/名称、调节系数、一/二/三级调节系数</li>
+                    <li>重复处理:主要诊断代码+省+市+机构等级相同则更新,否则新增</li>
+                    <li>模板规范:模板列名不可变更</li>
+                  </ul>
+                }
+                type="info"
+                showIcon
+              />
+            </Form>
+
+            <div style={{ marginTop: 24, textAlign: 'right' }}>
+              <Button onClick={handleCloseImport}>取消</Button>
+              <Button
+                type="primary"
+                style={{ marginLeft: 8 }}
+                onClick={handlePreviewImport}
+                loading={importLoading}
+                disabled={fileList.length === 0}
+              >
+                下一步:预览 <ArrowRightOutlined />
+              </Button>
+            </div>
+          </div>
+        )}
+
+        {/* 步骤2:数据预览 */}
+        {importStep === 1 && (
+          <div>
+            <Alert
+              message={
+                <Space>
+                  <span>数据概览:</span>
+                  <Typography.Text>共计 <Typography.Text strong>{previewStats.total}</Typography.Text> 条</Typography.Text>
+                  <Divider type="vertical" />
+                  <Typography.Text type="success">正常 <Typography.Text strong>{previewStats.valid}</Typography.Text> 条</Typography.Text>
+                  <Divider type="vertical" />
+                  <Typography.Text type="warning">重复 <Typography.Text strong>{previewStats.duplicate}</Typography.Text> 条</Typography.Text>
+                  <Divider type="vertical" />
+                  <Typography.Text type="danger">异常 <Typography.Text strong>{previewStats.invalid}</Typography.Text> 条</Typography.Text>
+                </Space>
+              }
+              type="info"
+              showIcon
+              style={{ marginBottom: 16 }}
+            />
+
+            <Table
+              columns={previewColumns}
+              dataSource={previewData}
+              rowKey="rowNum"
+              size="small"
+              scroll={{ y: 300 }}
+              pagination={false}
+            />
+
+            <div style={{ marginTop: 16 }}>
+              <Alert
+                message="确认导入后将执行以下操作"
+                description={
+                  <ul style={{ margin: 0, paddingLeft: 16 }}>
+                    <li>正常数据:直接导入</li>
+                    <li>重复数据:更新现有记录</li>
+                    <li>异常数据:跳过不导入</li>
+                  </ul>
+                }
+                type="warning"
+                showIcon
+              />
+            </div>
+
+            <div style={{ marginTop: 24, textAlign: 'right' }}>
+              <Button onClick={() => setImportStep(0)} icon={<ArrowLeftOutlined />}>
+                上一步
+              </Button>
+              <Button
+                type="primary"
+                style={{ marginLeft: 8 }}
+                onClick={handleConfirmImport}
+                loading={importLoading}
+              >
+                确认导入
+              </Button>
+            </div>
+          </div>
+        )}
+
+        {/* 步骤3:导入结果 */}
+        {importStep === 2 && importResult && (
+          <div>
+            <Result
+              status={importResult.failCount === 0 ? 'success' : 'warning'}
+              title={importResult.failCount === 0 ? '导入成功' : '导入完成(部分失败)'}
+              subTitle={`总计 ${importResult.totalCount} 条数据,成功 ${importResult.successCount} 条,失败 ${importResult.failCount} 条`}
+            />
+
+            <Row gutter={16} style={{ marginBottom: 16 }}>
+              <Col span={6}>
+                <Card size="small">
+                  <Statistic
+                    title="总记录数"
+                    value={importResult.totalCount}
+                  />
+                </Card>
+              </Col>
+              <Col span={6}>
+                <Card size="small">
+                  <Statistic
+                    title="成功导入"
+                    value={importResult.successCount}
+                    valueStyle={{ color: '#52c41a' }}
+                  />
+                </Card>
+              </Col>
+              <Col span={6}>
+                <Card size="small">
+                  <Statistic
+                    title="新增记录"
+                    value={importResult.newCount}
+                    valueStyle={{ color: '#1890ff' }}
+                  />
+                </Card>
+              </Col>
+              <Col span={6}>
+                <Card size="small">
+                  <Statistic
+                    title="更新记录"
+                    value={importResult.duplicateCount}
+                    valueStyle={{ color: '#faad14' }}
+                  />
+                </Card>
+              </Col>
+            </Row>
+
+            {importResult.failCount > 0 && (
+              <>
+                <Divider />
+                <Typography.Title level={5}>失败明细</Typography.Title>
+                <List
+                  size="small"
+                  bordered
+                  dataSource={importResult.failList}
+                  renderItem={item => (
+                    <List.Item>
+                      <Space>
+                        <Typography.Text type="secondary">行 {item.rowNum}</Typography.Text>
+                        <Typography.Text code>{item.principalDiagnosis || '空代码'}</Typography.Text>
+                        <Typography.Text type="danger">{item.errorMsg}</Typography.Text>
+                      </Space>
+                    </List.Item>
+                  )}
+                  style={{ maxHeight: 200, overflow: 'auto' }}
+                />
+              </>
+            )}
+
+            <div style={{ marginTop: 24, textAlign: 'right' }}>
+              <Button onClick={handleCloseImport} type="primary">
+                完成
+              </Button>
+            </div>
+          </div>
+        )}
+      </Modal>
     </div>
   );
 };

+ 0 - 0
src/pages/BasicData/CoreAlgorithmConfig.tsx → src/pages/BasicData/DRGCoreAlgorithmConfig.tsx