Yansen 2 years ago
parent
commit
9743e70b0b

+ 6
- 6
config/routes.js View File

178
         access: 'deviceJobDetail',
178
         access: 'deviceJobDetail',
179
         hideInMenu: true,
179
         hideInMenu: true,
180
       },
180
       },
181
-      {
182
-        path: '/iot/Subsoiler',
183
-        name: '深松机',
184
-        component: './SystemManagement/Subsoiler',
185
-        access: 'shensong',
186
-      },
181
+      // {
182
+      //   path: '/iot/Subsoiler',
183
+      //   name: '深松机',
184
+      //   component: './SystemManagement/Subsoiler',
185
+      //   access: 'shensong',
186
+      // },
187
       {
187
       {
188
         path: '/iot/Classification',
188
         path: '/iot/Classification',
189
         name: '外设分类',
189
         name: '外设分类',

+ 2
- 1
package.json View File

71
     "react-helmet-async": "^1.2.0",
71
     "react-helmet-async": "^1.2.0",
72
     "swiper": "^8.0.7",
72
     "swiper": "^8.0.7",
73
     "umi": "^3.5.0",
73
     "umi": "^3.5.0",
74
-    "web-control": "^1.0.2"
74
+    "web-control": "^1.0.2",
75
+    "xlsx": "https://cdn.sheetjs.com/xlsx-0.19.2/xlsx-0.19.2.tgz"
75
   },
76
   },
76
   "devDependencies": {
77
   "devDependencies": {
77
     "@ant-design/pro-cli": "^2.1.0",
78
     "@ant-design/pro-cli": "^2.1.0",

+ 41
- 0
src/pages/Machinery/Sensing/components/FeiFangForm.bak.jsx View File

1
+import React from 'react';
2
+import { Modal, Form, Input, Select, Button } from 'antd';
3
+
4
+const formItemLayout = { labelCol: { span: 6 }, wrapperCol: { span: 14 } };
5
+const FormItem = Form.Item;
6
+
7
+export default (props) => {
8
+  const { onCancel, onFinish } = props;
9
+
10
+  const [form] = Form.useForm();
11
+
12
+  return (
13
+    <Form {...formItemLayout} onFinish={onFinish} form={form}>
14
+      <FormItem
15
+        label="设备大类"
16
+        name="deviceKind"
17
+        initialValue="feifang"
18
+        rules={[{ required: true, message: '请选择设备大类' }]}
19
+      >
20
+        <Select placeholder="请选择">
21
+          <Select.Option value="feifang">飞防</Select.Option>
22
+        </Select>
23
+      </FormItem>
24
+      <FormItem
25
+        label="设备编号"
26
+        name="deviceNo"
27
+        rules={[{ required: true, message: '请输入设备编号' }]}
28
+      >
29
+        <Input placeholder="请输入" />
30
+      </FormItem>
31
+      <FormItem label=" " colon={false}>
32
+        <Button type="default" onClick={onCancel}>
33
+          取消
34
+        </Button>
35
+        <Button type="primary" htmlType="Submit" style={{ marginLeft: '2em' }}>
36
+          确认
37
+        </Button>
38
+      </FormItem>
39
+    </Form>
40
+  );
41
+};

+ 122
- 33
src/pages/Machinery/Sensing/components/FeiFangForm.jsx View File

1
 import React from 'react';
1
 import React from 'react';
2
-import { Modal, Form, Input, Select, Button } from 'antd';
2
+import { Upload, Spin, Collapse, Space, Typography, Button } from 'antd';
3
+import { InboxOutlined } from '@ant-design/icons';
4
+import { read, utils } from 'xlsx';
5
+import { addRawDevice } from '@/services/device';
3
 
6
 
4
-const formItemLayout = { labelCol: { span: 6 }, wrapperCol: { span: 14 } };
5
-const FormItem = Form.Item;
7
+const { Dragger } = Upload;
8
+const { Panel } = Collapse;
9
+const { Text } = Typography;
10
+const deviceKind = 'feifang';
6
 
11
 
7
-export default (props) => {
12
+export default React.forwardRef((props, ref) => {
8
   const { onCancel, onFinish } = props;
13
   const { onCancel, onFinish } = props;
9
 
14
 
10
-  const [form] = Form.useForm();
15
+  const [loading, setLoading] = React.useState(false);
16
+  const [finished, setFinished] = React.useState(false);
17
+  const [deviceNoList, setDeviceNoList] = React.useState([]);
18
+  const [okList, setOkList] = React.useState([]);
19
+  const [failList, setFailList] = React.useState([]);
20
+
21
+  const okListRef = React.useRef();
22
+  okListRef.current = okList;
23
+  const failListRef = React.useRef();
24
+  failListRef.current = failList;
25
+
26
+  const loadingTip = React.useMemo(() => {
27
+    return `正在处理 ${okList.length + failList.length} / ${deviceNoList.length}`;
28
+  }, [deviceNoList, okList, failList]);
29
+
30
+  const beforeUpload = (file) => {
31
+    const reader = new FileReader();
32
+    reader.onload = (e) => {
33
+      const wb = read(e.target.result);
34
+      // data 格式为 [{ "设备号": "xxx" }], 其中 xxx 为设备号
35
+      const data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]) || [];
36
+      const list = data.map((x) => x['设备号']);
37
+      setDeviceNoList(list || []);
38
+      setOkList([]);
39
+      setFailList([]);
40
+      setFinished(false);
41
+    };
42
+    reader.readAsArrayBuffer(file);
43
+
44
+    // 不上传
45
+    return false;
46
+  };
47
+
48
+  React.useEffect(() => {
49
+    if (!deviceNoList || !deviceNoList.length) return;
50
+
51
+    setLoading(true);
52
+    setOkList([]);
53
+    setFailList([]);
54
+
55
+    let i = 0;
56
+    for (let deviceNo of deviceNoList) {
57
+      addRawDevice({ deviceKind, deviceNo }, { showType: 0 })
58
+        .then(() => {
59
+          i += 1;
60
+          okListRef.current = okListRef.current.concat(deviceNo);
61
+          setOkList(okListRef.current);
62
+          setFinished(i >= deviceNoList.length);
63
+        })
64
+        .catch(() => {
65
+          i += 1;
66
+          failListRef.current = failListRef.current.concat(deviceNo);
67
+          setFailList(failListRef.current);
68
+          setFinished(i >= deviceNoList.length);
69
+        });
70
+    }
71
+  }, [deviceNoList]);
72
+
73
+  React.useImperativeHandle(
74
+    ref,
75
+    () => ({
76
+      refresh: () => {
77
+        setLoading(false);
78
+        setFinished(false);
79
+        setDeviceNoList([]);
80
+        setOkList([]);
81
+        setFailList([]);
82
+      },
83
+    }),
84
+    [],
85
+  );
11
 
86
 
12
   return (
87
   return (
13
-    <Form {...formItemLayout} onFinish={onFinish} form={form}>
14
-      <FormItem
15
-        label="设备大类"
16
-        name="deviceKind"
17
-        initialValue="feifang"
18
-        rules={[{ required: true, message: '请选择设备大类' }]}
19
-      >
20
-        <Select placeholder="请选择">
21
-          <Select.Option value="feifang">飞防</Select.Option>
22
-        </Select>
23
-      </FormItem>
24
-      <FormItem
25
-        label="设备编号"
26
-        name="deviceNo"
27
-        rules={[{ required: true, message: '请输入设备编号' }]}
28
-      >
29
-        <Input placeholder="请输入" />
30
-      </FormItem>
31
-      <FormItem label=" " colon={false}>
32
-        <Button type="default" onClick={onCancel}>
33
-          取消
34
-        </Button>
35
-        <Button type="primary" htmlType="Submit" style={{ marginLeft: '2em' }}>
36
-          确认
37
-        </Button>
38
-      </FormItem>
39
-    </Form>
88
+    <div>
89
+      {!finished && (
90
+        <Spin spinning={loading} tip={loadingTip}>
91
+          <Dragger accept=".xls,.xlsx" fileList={[]} beforeUpload={beforeUpload}>
92
+            <p className="ant-upload-drag-icon">
93
+              <InboxOutlined />
94
+            </p>
95
+            <p className="ant-upload-text">点击或拖拽 Excel 文件到此区域</p>
96
+            <p className="ant-upload-hint">
97
+              Excel 要求只有一列, 标题为"设备号"(不带引号), 数据从第二行开始
98
+            </p>
99
+          </Dragger>
100
+        </Spin>
101
+      )}
102
+      {finished && (
103
+        <Space direction="vertical">
104
+          <Collapse collapsible="header" defaultActiveKey={['1']}>
105
+            <Panel header="成功设备" key="1">
106
+              <Typography>
107
+                {okList.map((x) => (
108
+                  <Text copyable key={x}>
109
+                    {x}
110
+                  </Text>
111
+                ))}
112
+              </Typography>
113
+            </Panel>
114
+          </Collapse>
115
+          <Collapse collapsible="header" defaultActiveKey={['1']}>
116
+            <Panel header="失败设备" key="1">
117
+              <Typography>
118
+                {failList.map((x) => (
119
+                  <Text copyable key={x}>
120
+                    {x}
121
+                  </Text>
122
+                ))}
123
+              </Typography>
124
+            </Panel>
125
+          </Collapse>
126
+        </Space>
127
+      )}
128
+    </div>
40
   );
129
   );
41
-};
130
+});

+ 12
- 3
src/pages/Machinery/Sensing/index.jsx View File

22
   const [device, setDevice] = useState();
22
   const [device, setDevice] = useState();
23
   const listRef = useRef();
23
   const listRef = useRef();
24
   const actRef = useRef();
24
   const actRef = useRef();
25
+  const ffRef = useRef();
25
 
26
 
26
   const modalTitle = actRef.current === 'add' ? '新增设备' : '绑定农机';
27
   const modalTitle = actRef.current === 'add' ? '新增设备' : '绑定农机';
27
 
28
 
132
       });
133
       });
133
   };
134
   };
134
 
135
 
136
+  const onHide = () => {
137
+    setOpen(false);
138
+    if (ffRef.current) {
139
+      ffRef.current.refresh();
140
+    }
141
+  };
142
+
135
   // 设备操作
143
   // 设备操作
136
   const onOperate = (row, act) => {
144
   const onOperate = (row, act) => {
137
     actRef.current = act;
145
     actRef.current = act;
168
             同步飞防
176
             同步飞防
169
           </Button>,
177
           </Button>,
170
           <Button ghost key={4} type="primary" loading={syncing3} onClick={onSyncMachine}>
178
           <Button ghost key={4} type="primary" loading={syncing3} onClick={onSyncMachine}>
171
-            同步农机
179
+            绑定农机
172
           </Button>,
180
           </Button>,
173
         ]}
181
         ]}
174
       />
182
       />
175
       <Modal
183
       <Modal
184
+        width={640}
176
         title={modalTitle}
185
         title={modalTitle}
177
         visible={open}
186
         visible={open}
178
         maskClosable={false}
187
         maskClosable={false}
179
         footer={null}
188
         footer={null}
180
-        onCancel={() => setOpen(false)}
189
+        onCancel={onHide}
181
       >
190
       >
182
         {actRef.current === 'add' ? (
191
         {actRef.current === 'add' ? (
183
-          <FeiFangForm onCancel={() => setOpen(false)} onFinish={onAddDevice} />
192
+          <FeiFangForm ref={ffRef} onCancel={() => setOpen(false)} onFinish={onAddDevice} />
184
         ) : (
193
         ) : (
185
           <MaBind onCancel={() => setOpen(false)} onFinish={onBind} />
194
           <MaBind onCancel={() => setOpen(false)} onFinish={onBind} />
186
         )}
195
         )}

+ 2
- 1
src/services/device.js View File

69
  * 增加设备
69
  * 增加设备
70
  * @returns
70
  * @returns
71
  */
71
  */
72
-export const addRawDevice = (data) => request('/raw-device', { method: 'POST', data });
72
+export const addRawDevice = (data, options) =>
73
+  request('/raw-device', { method: 'POST', data, ...(options || {}) });
73
 
74
 
74
 /**
75
 /**
75
  * 删除设备
76
  * 删除设备