张延森 3 år sedan
förälder
incheckning
ae727b1775

+ 1
- 0
.env Visa fil

@@ -0,0 +1 @@
1
+PORT=8001

+ 145
- 0
src/pages/Medical/Test/Batch/DoExcel.jsx Visa fil

@@ -0,0 +1,145 @@
1
+import React, { useState, useRef } from 'react';
2
+import { Alert, Button, message, Space, Modal, Table } from 'antd';
3
+import { FileExcelOutlined, SaveOutlined, RedoOutlined } from '@ant-design/icons';
4
+import { saveTestExcel } from '@/services/test';
5
+import Step from './components/Step'
6
+import { read } from './excel'
7
+
8
+const fullWidth = { width: '100%' }
9
+
10
+export default (props) => {
11
+  const { schoolId } = props
12
+
13
+  const [visible, setVisible] = useState(false)
14
+  const [reading, setReading] = useState(false)
15
+  const [submitting, setSubmitting] = useState(false)
16
+
17
+  const workbookRef = useRef()
18
+  const [dataSource, setDataSource] = useState([])
19
+
20
+  const columns = [
21
+    {
22
+      key: 'index',
23
+      title: '序号',
24
+      width: 200,
25
+      align: 'center',
26
+      render: (t,r,i) => i + 1,
27
+    },
28
+    {
29
+      key: 'testNo',
30
+      dataIndex: 'testNo',
31
+      title: '体检号',
32
+      align: 'center',
33
+    },
34
+    {
35
+      key: 'studentNo',
36
+      dataIndex: 'studentNo',
37
+      title: '学号',
38
+      align: 'center',
39
+    },
40
+  ]
41
+
42
+  const handleFile = () => {
43
+    const input = document.createElement('input')
44
+    input.type = 'file'
45
+    input.accept = 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
46
+    input.onchange = (e) => {
47
+      const file = e.target.files[0]
48
+      if (!file) {
49
+        message.warning('请选择文件')
50
+      }
51
+
52
+      setReading(true)
53
+      read(file).then((res) => {
54
+        workbookRef.current = res
55
+        setReading(false)
56
+        setVisible(true)
57
+      }).catch((err) => {
58
+        console.error(err)
59
+        setReading(false)
60
+      })
61
+    }
62
+    input.click()
63
+  }
64
+  
65
+
66
+  const handleSettingChange = (e) => {
67
+    setVisible(false)
68
+
69
+    const { dataStartRow, sheet, cols } = e
70
+
71
+    const [, end] = sheet['!ref'].split(':')
72
+    const maxRow = /\d+/.exec(end)[0] - 0
73
+    const arr = []
74
+    for (let i = dataStartRow; i <= maxRow; i += 1) {
75
+      const testNo = sheet[`${cols.testNoCol}${i}`]?.v
76
+      const studentNo = sheet[`${cols.studentNoCol}${i}`]?.v
77
+
78
+      arr.push({
79
+        testNo,
80
+        studentNo,
81
+        schoolId,
82
+      })
83
+    }
84
+
85
+    setDataSource(arr)
86
+  }
87
+  
88
+  const handleSubmit = () => {
89
+    setSubmitting(true)
90
+    saveTestExcel(dataSource).then(() => {
91
+      setSubmitting(false)
92
+      setDataSource([])
93
+      message.success("保存入库成功");
94
+    }).catch((err) => {
95
+      console.error(err);
96
+      message.error(err.message);
97
+      setSubmitting(false)
98
+    })
99
+  }
100
+
101
+  return (
102
+    <div>
103
+      <Space direction="vertical" size="large" style={fullWidth}>
104
+        <Alert description="Excel 文件数据不要超过 200 列, 2000 行。如果出现未知错误, 请退出重试几次" type="warning" closable />
105
+        <Space direction="horizontal" size="large" style={fullWidth}>
106
+          <Button
107
+            ghost
108
+            type="primary"
109
+            loading={reading}
110
+            disabled={!schoolId}
111
+            icon={<FileExcelOutlined />}
112
+            onClick={handleFile}
113
+          >上传 Excel 文件</Button>
114
+
115
+          <Button
116
+            ghost
117
+            type="primary"
118
+            disabled={!dataSource.length}
119
+            icon={<RedoOutlined />}
120
+            onClick={() => setDataSource([])}
121
+          >重置</Button>
122
+          
123
+          <Button
124
+            type="primary"
125
+            loading={submitting}
126
+            disabled={!dataSource.length}
127
+            icon={<SaveOutlined />}
128
+            onClick={handleSubmit}
129
+          >保存入库</Button>
130
+        </Space>
131
+        <Table columns={columns} dataSource={dataSource} rowKey="testNo" pagination={false} />
132
+      </Space>
133
+      <Modal
134
+        title="设置数据内容"
135
+        visible={visible}
136
+        onCancel={() => setVisible(false)}
137
+        footer={null}
138
+        width={480}
139
+        maskClosable={false}
140
+      >
141
+        <Step workbook={workbookRef.current} onChange={handleSettingChange} />
142
+      </Modal>
143
+    </div>
144
+  )
145
+}

+ 154
- 0
src/pages/Medical/Test/Batch/DoPdf.jsx Visa fil

@@ -0,0 +1,154 @@
1
+import React, { useState } from 'react';
2
+import { Button, Space, Table, Progress, message } from 'antd';
3
+import { FileZipOutlined, SaveOutlined, RedoOutlined } from '@ant-design/icons';
4
+import { uploadPdf } from '@/utils/uploadFile';
5
+import { saveTestPDF } from '@/services/test';
6
+
7
+const fullWidth = { width: '100%' }
8
+
9
+const getFileName = (url) => {
10
+  const parts = url.split('/');
11
+  return parts[parts.length - 1];
12
+}
13
+
14
+export default (props) => {
15
+  const { schoolId } = props
16
+  
17
+  const [loading, setLoading] = useState(false)
18
+  const [submitting, setSubmitting] = useState(false)
19
+  const [percent, setPercent] = useState(0)
20
+  const [dataSource, setDataSource] = useState([])
21
+
22
+  const columns = [
23
+    {
24
+      key: 'index',
25
+      title: '序号',
26
+      width: 200,
27
+      align: 'center',
28
+      render: (t,r,i) => i + 1,
29
+    },
30
+    {
31
+      key: 'studentName',
32
+      dataIndex: 'studentName',
33
+      title: '姓名',
34
+      align: 'center',
35
+    },
36
+    {
37
+      key: 'testNo',
38
+      dataIndex: 'testNo',
39
+      title: '体检编号',
40
+      align: 'center',
41
+    },
42
+    {
43
+      key: 'fileName',
44
+      dataIndex: 'fileName',
45
+      title: '文件',
46
+      align: 'center',
47
+      render: (t, r) => <a href={r.url}>{t}</a>
48
+    },
49
+  ];
50
+
51
+  const handleFile = () => {
52
+    const input = document.createElement('input')
53
+    input.type = 'file'
54
+    input.accept = 'application/pdf'
55
+    input.multiple = true
56
+    input.onchange = (e) => {
57
+      const { files } = e.target
58
+      if (!files || !files.length) {
59
+        message.warning('请选择文件')
60
+      }
61
+
62
+      const len = files.length;
63
+      const list = [];
64
+
65
+      let i = 0;
66
+      let j = 0;
67
+      setLoading(true);
68
+      setPercent(0);
69
+      const t = setInterval(() => {
70
+        if (i >= len) {
71
+          clearInterval(t);
72
+          const newLst = list.map((url) => {
73
+            const fileName = getFileName(url)
74
+            const testNo = /^[^-]+/.exec(fileName)[0]
75
+            const studentName = /-(.+)\.pdf/.exec(fileName)[1]
76
+
77
+            return {
78
+              schoolId,
79
+              testNo,
80
+              studentName: decodeURIComponent(studentName),
81
+              fileName: decodeURIComponent(fileName),
82
+              url,
83
+            }
84
+          })
85
+
86
+          setDataSource([...dataSource, ...newLst])
87
+          setLoading(false)
88
+          return;
89
+        }
90
+
91
+        uploadPdf(files[i]).then((url) => {
92
+          j += 1;
93
+          setPercent(j * 100 / len)
94
+          list.push(url);
95
+        }).catch((err) => {
96
+          clearInterval(t);
97
+          console.error(err);
98
+          message.error(err.message);
99
+        })
100
+        i += 1;
101
+      }, 500);
102
+    }
103
+    input.click()
104
+  }
105
+
106
+  const handleSubmit = () => {
107
+    setSubmitting(true)
108
+    saveTestPDF(dataSource).then(() => {
109
+      setSubmitting(false)
110
+      setDataSource([])
111
+      message.success("保存入库成功");
112
+    }).catch((err) => {
113
+      console.error(err);
114
+      message.error(err.message);
115
+      setSubmitting(false)
116
+    })
117
+  }
118
+
119
+  return (
120
+    <Space direction="vertical" size="large" style={fullWidth}>
121
+      <Space direction="horizontal" size="large" style={fullWidth}>
122
+        <Button
123
+          ghost
124
+          type="primary"
125
+          loading={loading}
126
+          disabled={!schoolId}
127
+          icon={<FileZipOutlined />}
128
+          onClick={handleFile}
129
+        >上传 PDF 文件</Button>
130
+
131
+        <Button
132
+          ghost
133
+          type="primary"
134
+          disabled={!dataSource.length}
135
+          icon={<RedoOutlined />}
136
+          onClick={() => setDataSource([])}
137
+        >重置</Button>
138
+        
139
+        <Button
140
+          type="primary"
141
+          loading={submitting}
142
+          disabled={!dataSource.length}
143
+          icon={<SaveOutlined />}
144
+          onClick={handleSubmit}
145
+        >保存入库</Button>
146
+
147
+        {
148
+          loading && <Progress style={{width: '400px'}} percent={percent} showInfo={false}/>
149
+        }
150
+      </Space>
151
+      <Table columns={columns} dataSource={dataSource} rowKey="testNo" pagination={false} />
152
+    </Space>
153
+  )
154
+}

src/pages/Medical/Test/Batch/Step.jsx → src/pages/Medical/Test/Batch/components/Step.jsx Visa fil

@@ -1,6 +1,6 @@
1 1
 import React, { useState, useRef } from 'react'
2 2
 import { Row, Col, Button, InputNumber, Select, Carousel, Typography } from 'antd'
3
-import { getRangeOfSheet, getAllCols } from './excel'
3
+import { getRangeOfSheet, getAllCols } from '../excel'
4 4
 import styles from './style.less'
5 5
 
6 6
 const { Title } = Typography;

src/pages/Medical/Test/Batch/style.less → src/pages/Medical/Test/Batch/components/style.less Visa fil


+ 13
- 128
src/pages/Medical/Test/Batch/index.jsx Visa fil

@@ -1,106 +1,17 @@
1 1
 import React, { useRef, useState } from 'react';
2 2
 import { PageContainer } from '@ant-design/pro-layout';
3
-import { Alert, Button, Card, message, Space, Modal, Table } from 'antd';
4
-import { FileExcelOutlined, SaveOutlined, FileZipOutlined } from '@ant-design/icons';
3
+import { Card, Space, Tabs } from 'antd';
5 4
 import School from '@/components/School';
6
-import Step from './Step'
7
-import { read } from './excel'
5
+import Excel from './DoExcel';
6
+import Pdf from './DoPdf';
8 7
 
9 8
 export default (props) => {
10 9
   
11
-  const [visible, setVisible] = useState(false)
12 10
   const [schoolId, setSchoolId] = useState()
13
-  const [reading, setReading] = useState(false)
14
-
15
-  const workbookRef = useRef()
16
-  const [dataSource, setDataSource] = useState([])
17
-  const [zipFile, setZipFile] = useState()
18
-
19
-  const columns = [
20
-    {
21
-      key: 'index',
22
-      title: '序号',
23
-      width: 200,
24
-      align: 'center',
25
-      render: (t,r,i) => i + 1,
26
-    },
27
-    {
28
-      key: 'testNo',
29
-      dataIndex: 'testNo',
30
-      title: '体检号',
31
-      align: 'center',
32
-    },
33
-    {
34
-      key: 'studentNo',
35
-      dataIndex: 'studentNo',
36
-      title: '学号',
37
-      align: 'center',
38
-    },
39
-  ]
40
-
41
-  const handleFile = () => {
42
-    const input = document.createElement('input')
43
-    input.type = 'file'
44
-    input.accept = 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
45
-    input.onchange = (e) => {
46
-      const file = e.target.files[0]
47
-      if (!file) {
48
-        message.warning('请选择文件')
49
-      }
50
-
51
-      setReading(true)
52
-      read(file).then((res) => {
53
-        workbookRef.current = res
54
-        setReading(false)
55
-        setVisible(true)
56
-      }).catch((err) => {
57
-        console.error(err)
58
-        setReading(false)
59
-      })
60
-    }
61
-    input.click()
62
-  }
63
-
64
-  const handleZipFile = () => {
65
-    const input = document.createElement('input')
66
-    input.type = 'file'
67
-    input.accept = 'application/zip'
68
-    input.onchange = (e) => {
69
-      const file = e.target.files[0]
70
-      if (!file) {
71
-        message.warning('请选择文件')
72
-      }
73
-      setZipFile(file)
74
-    }
75
-    input.click()
76
-
77
-  }
78
-
79
-  const handleSettingChange = (e) => {
80
-    setVisible(false)
81
-
82
-    const { dataStartRow, sheet, cols } = e
83
-
84
-    const [, end] = sheet['!ref'].split(':')
85
-    const maxRow = /\d+/.exec(end)[0] - 0
86
-    const arr = []
87
-    for (let i = dataStartRow; i <= maxRow; i += 1) {
88
-      const testNo = sheet[`${cols.testNoCol}${i}`]?.v
89
-      const studentNo = sheet[`${cols.studentNoCol}${i}`]?.v
90
-
91
-      arr.push({
92
-        testNo,
93
-        studentNo, 
94
-      })
95
-    }
96
-
97
-    setDataSource(arr)
98
-  }
99 11
 
100 12
   return (
101 13
     <PageContainer>
102 14
       <Space direction="vertical" size="large" style={{width: '100%'}}>
103
-        <Alert message="友情提示" description="导入文件数据不要超过 200 列, 6000 行。如果出现未知错误, 请退出重试几次" type="info" closable />
104 15
         <Card>
105 16
           <Space direction="horizontal" size="large">
106 17
             <School
@@ -111,46 +22,20 @@ export default (props) => {
111 22
               style={{width: '300px'}}
112 23
               placeholder="请选择学校"
113 24
             ></School>
114
-
115
-            <Button
116
-              ghost
117
-              type="primary"
118
-              loading={reading}
119
-              disabled={!schoolId}
120
-              icon={<FileExcelOutlined />}
121
-              onClick={handleFile}
122
-            >上传 Excel 文件</Button>
123
-
124
-            <Button
125
-              ghost
126
-              type="primary"
127
-              disabled={!dataSource ||!dataSource.length}
128
-              icon={<FileZipOutlined />}
129
-              onClick={handleZipFile}
130
-            >上传 ZIP 文件</Button>
131
-            
132
-            <Button
133
-              type="primary"
134
-              loading={reading}
135
-              disabled={!zipFile}
136
-              icon={<SaveOutlined />}
137
-              onClick={handleFile}
138
-            >保存入库</Button>
139 25
           </Space>
140 26
         </Card>
141 27
 
142
-        <Table columns={columns} dataSource={dataSource} rowKey="testNo" pagination={false} />
28
+        <Card>
29
+          <Tabs defaultActiveKey="1">
30
+            <Tabs.TabPane tab="上传 Excel" key="1">
31
+              <Excel schoolId={schoolId} />
32
+            </Tabs.TabPane>
33
+            <Tabs.TabPane tab="上传 pdf"  key="2">
34
+              <Pdf schoolId={schoolId} />
35
+            </Tabs.TabPane>
36
+          </Tabs>
37
+        </Card>
143 38
       </Space>
144
-      <Modal
145
-        title="设置数据内容"
146
-        visible={visible}
147
-        onCancel={() => setVisible(false)}
148
-        footer={null}
149
-        width={480}
150
-        maskClosable={false}
151
-      >
152
-        <Step workbook={workbookRef.current} onChange={handleSettingChange} />
153
-      </Modal>
154 39
     </PageContainer>
155 40
   )
156 41
 }

+ 5
- 0
src/services/test.js Visa fil

@@ -0,0 +1,5 @@
1
+import request from '@/utils/request';
2
+
3
+export const saveTestPDF = (formData) => request('/taTestLog/upload/pdf', { data: formData, method: 'POST' } )
4
+
5
+export const saveTestExcel = (formData) => request('/taTestLog/upload/excel', { data: formData, method: 'POST' } )

+ 3
- 3
src/utils/request.js Visa fil

@@ -31,17 +31,17 @@ const errorHandler = (error) => {
31 31
       message: `请求错误 ${status}: ${url}`,
32 32
       description: errorText,
33 33
     });
34
+    throw new Error(`请求错误 ${status}`);
34 35
   } else if (error.code && error.message) {
35 36
     throw new Error(error.message);
36 37
   } else {
37 38
     console.error(error)
38 39
     notification.error({
39 40
       description: '您的网络发生异常,无法连接服务器',
40
-      message: '网络异常',
41
+      message: error?.status || '网络异常',
41 42
     });
43
+    throw new Error('网络异常');
42 44
   }
43
-
44
-  return response;
45 45
 };
46 46
 /** 配置request请求时的默认参数 */
47 47
 

+ 34
- 0
src/utils/uploadFile.js Visa fil

@@ -1,4 +1,5 @@
1 1
 import OSS from 'ali-oss';
2
+import moment from 'moment';
2 3
 import request from './request';
3 4
 
4 5
 // 通过 OSS STS 模式上传
@@ -99,3 +100,36 @@ export function uploadImage(file) {
99 100
     }
100 101
   });
101 102
 }
103
+
104
+
105
+export function uploadPdf(file) {
106
+  return new Promise((resolve, reject) => {
107
+    const upload = () => {
108
+      const data = new FormData();
109
+      data.append('file', file);
110
+      const today = moment().format('YYYY-MM-DD')
111
+      const fileName = `${stsInfo.path}/test-pdf/${today}/${file.name}`;
112
+
113
+      ossClient
114
+        .put(fileName, file)
115
+        .then((res) => {
116
+          resolve(res.url);
117
+        })
118
+        .catch((e2) => {
119
+          reject(e2);
120
+        });
121
+    };
122
+
123
+    if (!ossClient) {
124
+      getOssClient()
125
+        .then(() => {
126
+          upload();
127
+        })
128
+        .catch((e) => {
129
+          reject(e);
130
+        });
131
+    } else {
132
+      upload();
133
+    }
134
+  });
135
+}