Your Name 3 years ago
parent
commit
064e0cf490

+ 8
- 1
config/routes.js View File

167
                     menuCode: 'medical.test',
167
                     menuCode: 'medical.test',
168
                     hideInMenu: true,
168
                     hideInMenu: true,
169
                     component: './Medical/Test/Edit',
169
                     component: './Medical/Test/Edit',
170
-                  },              
170
+                  },
171
+                  {
172
+                    path: '/medical/test/batch',
173
+                    name: '批量处理',
174
+                    menuCode: 'medical.batch',
175
+                    hideInMenu: true,
176
+                    component: './Medical/Test/Batch',
177
+                  },
171
                   {
178
                   {
172
                     path: '/medical/hospital',
179
                     path: '/medical/hospital',
173
                     name: '诊室管理',
180
                     name: '诊室管理',

+ 2
- 1
package.json View File

67
     "react-sortable-hoc": "^2.0.0",
67
     "react-sortable-hoc": "^2.0.0",
68
     "umi": "^3.4.1",
68
     "umi": "^3.4.1",
69
     "umi-request": "^1.0.8",
69
     "umi-request": "^1.0.8",
70
-    "wangeditor": "^4.6.15"
70
+    "wangeditor": "^4.6.15",
71
+    "xlsx": "^0.17.1"
71
   },
72
   },
72
   "devDependencies": {
73
   "devDependencies": {
73
     "@ant-design/pro-cli": "^1.0.28",
74
     "@ant-design/pro-cli": "^1.0.28",

+ 64
- 0
src/pages/Medical/Test/Batch/Step.jsx View File

1
+import React, { useState, useRef } from 'react'
2
+import { Button, Checkbox, Select, Space, Carousel, Typography } from 'antd'
3
+import { getRangeOfSheet, getAllCols } from './excel'
4
+import styles from './style.less'
5
+
6
+const { Title } = Typography;
7
+const { Option } = Select;
8
+
9
+export default (props) => {
10
+  const { workbook } = props
11
+
12
+  const sliderRef = useRef()
13
+  const [sheetName, setSheetName] = useState()
14
+  const sheetRef = useRef()
15
+  const [allCols, setAllCols] = useState([])
16
+  const [columns, setColumns] = useState()
17
+
18
+  const handleSheetSelect = (nm) => {
19
+    setSheetName(nm)
20
+    sheetRef.current = workbook.Sheets[nm]
21
+
22
+    const sheetRange = getRangeOfSheet(sheetRef.current)
23
+    const cols = getAllCols(sheetRange.startCol, sheetRange.endCol)
24
+    setAllCols(cols)
25
+  }
26
+
27
+  const handleColChange = (cols) => {
28
+    setColumns(cols)
29
+  }
30
+
31
+  const next = () => {
32
+    sliderRef.current.next()
33
+  }
34
+
35
+  return (
36
+    <div>
37
+      <Carousel ref={sliderRef}>
38
+        <div className={styles['step-box']}>
39
+          <Space direction="vertical" size="large" style={{width: '100%'}}>
40
+            <Title level={4}>请选择 sheet</Title>
41
+            <Select value={sheetName} onChange={handleSheetSelect}  style={{minWidth: '200px'}}>
42
+              {
43
+                (workbook.SheetNames || []).map((nm) => (<Option key={nm} value={nm}>{nm}</Option>))
44
+              }
45
+            </Select>
46
+            <Button disabled={!sheetName} type="primary" onClick={next}>下一步</Button>
47
+          </Space>
48
+        </div>
49
+        
50
+        <div className={styles['step-box']}>
51
+          <Space direction="vertical" size="large" style={{width: '100%'}}>
52
+            <Title level={4}>请选择 数据列</Title>
53
+            <Checkbox.Group style={{ width: '100%' }} value={columns} onChange={handleColChange}>
54
+              {
55
+                allCols.map((col) => (<div key={col}><Checkbox value={col}>{col}</Checkbox></div>))
56
+              }
57
+            </Checkbox.Group>
58
+            <Button type="primary" disabled={!columns || !columns.length} onClick={next}>下一步</Button>
59
+          </Space>
60
+        </div>
61
+      </Carousel>
62
+    </div>
63
+  )
64
+}

+ 40
- 0
src/pages/Medical/Test/Batch/excel.js View File

1
+import XLSX from 'xlsx'
2
+import { o2s, s2o } from './number26'
3
+
4
+export function read(file) {
5
+  return new Promise((resolve, reject) => {
6
+
7
+    const reader = new FileReader()
8
+    reader.onload = (e) => {
9
+      const data = new Uint8Array(e.target.result)
10
+  
11
+      const workbook = XLSX.read(data, {type: 'array'})
12
+      resolve(workbook)
13
+    }
14
+    reader.readAsArrayBuffer(file);
15
+  })
16
+}
17
+
18
+export function getRangeOfSheet(sheet) {
19
+  const [from, to] = sheet['!ref'].split(':')
20
+
21
+  const startCol = (/[a-zA-Z]+/.exec(from) || [])[0]
22
+  const endCol = (/[a-zA-Z]+/.exec(to) || [])[0]
23
+  const startRow = (/\d+/.exec(from) || [])[0]
24
+  const endRow = (/\d+/.exec(to) || [])[0]
25
+
26
+  return {
27
+    startCol,
28
+    endCol,
29
+    startRow,
30
+    endRow,
31
+  }
32
+}
33
+
34
+export function getAllCols(start, end) {
35
+  const i = s2o(start);
36
+  const j = s2o(end);
37
+  const len = j - i + 1;
38
+
39
+  return (new Array(len)).fill('*').map((_, inx) => o2s(inx + i))
40
+}

+ 77
- 0
src/pages/Medical/Test/Batch/index.jsx View File

1
+import React, { useRef, useState } from 'react';
2
+import { PageContainer } from '@ant-design/pro-layout';
3
+import { Alert, Button, Card, message, Space, Modal } from 'antd';
4
+import { UploadOutlined } from '@ant-design/icons';
5
+import School from '@/components/School';
6
+import Step from './Step'
7
+import { read } from './excel'
8
+
9
+export default (props) => {
10
+  
11
+  const [visible, setVisible] = useState(false)
12
+  const [schoolId, setSchoolId] = useState()
13
+  const [reading, setReading] = useState(false)
14
+
15
+  const workbookRef = useRef()
16
+
17
+  const handleFile = () => {
18
+    const input = document.createElement('input')
19
+    input.type = 'file'
20
+    input.accept = 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
21
+    input.onchange = (e) => {
22
+      const file = e.target.files[0]
23
+      if (!file) {
24
+        message.warning('请选择文件')
25
+      }
26
+
27
+      setReading(true)
28
+      read(file).then((res) => {
29
+        workbookRef.current = res
30
+        setReading(false)
31
+        setVisible(true)
32
+      }).catch((err) => {
33
+        console.error(err)
34
+        setReading(false)
35
+      })
36
+    }
37
+    input.click()
38
+  }
39
+
40
+  return (
41
+    <PageContainer>
42
+      <Space direction="vertical" size="large" style={{width: '100%'}}>
43
+        <Alert message="友情提示" description="导入文件数据不要超过 200 列, 6000 行" type="info" closable />
44
+        <Card>
45
+          <Space direction="horizontal" size="large">
46
+            <School
47
+              autoDefault={false}
48
+              value={schoolId}
49
+              onChange={setSchoolId}
50
+              allowClear
51
+              style={{width: '300px'}}
52
+              placeholder="请选择学校"
53
+            ></School>
54
+
55
+            <Button
56
+              ghost
57
+              type="primary"
58
+              loading={reading}
59
+              disabled={!schoolId}
60
+              icon={<UploadOutlined />}
61
+              onClick={handleFile}
62
+            >导入</Button>
63
+          </Space>
64
+        </Card>
65
+      </Space>
66
+      <Modal
67
+        title="设置数据内容"
68
+        visible={visible}
69
+        onCancel={() => setVisible(false)}
70
+        footer={null}
71
+        maskClosable={false}
72
+      >
73
+        <Step workbook={workbookRef.current} />
74
+      </Modal>
75
+    </PageContainer>
76
+  )
77
+}

+ 39
- 0
src/pages/Medical/Test/Batch/number26.js View File

1
+/**
2
+ * 26 进制与 10 进制的转换
3
+ */
4
+
5
+/**
6
+ * 十进制转26进制
7
+ * @param {*} n 
8
+ */
9
+export function o2s(num) {
10
+  let n = num;
11
+  let str = '';
12
+  while (n > 0) {
13
+    let m = n % 26;
14
+    if (m === 0) {
15
+      m = 26;
16
+    }
17
+    str = String.fromCharCode(m + 64) + str;
18
+    n = (n - m) / 26;
19
+  }
20
+
21
+  return str;
22
+}
23
+
24
+/**
25
+ * 26进制转10进制
26
+ * @param {*} str 
27
+ * @returns 
28
+ */
29
+export function s2o(str) {
30
+  let n = 0;
31
+
32
+  for (let i = str.length - 1, j = 1; i >= 0; i -= 1, j *= 26) {
33
+    const c = str.charCodeAt(i)
34
+    
35
+    n += (c - 64) * j;
36
+  }
37
+
38
+  return n;
39
+}

+ 5
- 0
src/pages/Medical/Test/Batch/style.less View File

1
+
2
+.step-box {
3
+  width: 800px;
4
+  min-height: 600px;
5
+}

+ 11
- 5
src/pages/Medical/Test/List/index.jsx View File

2
 import { connect, history } from 'umi';
2
 import { connect, history } from 'umi';
3
 import { PageContainer } from '@ant-design/pro-layout';
3
 import { PageContainer } from '@ant-design/pro-layout';
4
 import ProTable from '@ant-design/pro-table';
4
 import ProTable from '@ant-design/pro-table';
5
-import { PlusOutlined } from '@ant-design/icons';
5
+import { PlusOutlined, GroupOutlined } from '@ant-design/icons';
6
 import { Button,  Space,Image } from 'antd';
6
 import { Button,  Space,Image } from 'antd';
7
 import  { queryTable } from '@/utils/request';
7
 import  { queryTable } from '@/utils/request';
8
 import School from '@/components/School';
8
 import School from '@/components/School';
14
   const [abc, setA] = useState();
14
   const [abc, setA] = useState();
15
   
15
   
16
   const ref = useRef();
16
   const ref = useRef();
17
+
17
   const handleMedicalClick = useCallback((id) => {
18
   const handleMedicalClick = useCallback((id) => {
18
     history.push(id ? `/medical/Test/edit?id=${id}&schoolId=${school?.schoolId}` : `/medical/Test/edit?schoolId=${school?.schoolId}`);
19
     history.push(id ? `/medical/Test/edit?id=${id}&schoolId=${school?.schoolId}` : `/medical/Test/edit?schoolId=${school?.schoolId}`);
19
   }, []);
20
   }, []);
20
 
21
 
21
-  const actions = [   
22
-    <Button key="button" icon={<PlusOutlined />} type="primary" onClick={() => handleMedicalClick()}>
22
+  const handleBatch = () => {
23
+    history.push('/medical/Test/batch')
24
+  }
25
+
26
+  const actions = [
27
+    <Button key="batch" icon={<GroupOutlined />} type="primary" ghost onClick={handleBatch}>
28
+      批量处理
29
+    </Button>,
30
+    <Button key="new" icon={<PlusOutlined />} type="primary" onClick={() => handleMedicalClick()}>
23
       新建
31
       新建
24
     </Button>,
32
     </Button>,
25
   ];
33
   ];
128
         }}
136
         }}
129
         toolBarRender={() => actions}
137
         toolBarRender={() => actions}
130
       />
138
       />
131
-     
132
-   
133
     </PageContainer>
139
     </PageContainer>
134
   );
140
   );
135
 };
141
 };