张延森 3 年之前
父節點
當前提交
200db99b8c

+ 88
- 45
src/pages/building/Edit/Basic/index.jsx 查看文件

@@ -1,5 +1,5 @@
1 1
 import React, { useState } from 'react'
2
-import { Button, Row, Col, DatePicker, Form, Input, InputNumber, Radio, notification, Select } from 'antd'
2
+import { Button, Spin, DatePicker, Form, Input, InputNumber, Radio, notification, Select } from 'antd'
3 3
 import FileUpload from '@/components/XForm/FileUpload'
4 4
 import ImageUpload from '@/components/XForm/ImageUpload'
5 5
 import ImageListUpload from '@/components/XForm/ImageListUpload'
@@ -12,15 +12,27 @@ import OpenTimePicker from '../components/OpenTimePicker'
12 12
 import EditableArround from '../components/EditableArround'
13 13
 import FormGroupItem, { gourpItemLayout } from '../components/FormGroupItem'
14 14
 import { formItemLayout, validMinNum } from '../utils'
15
+import useTags from './tag'
15 16
 
16 17
 const fullWidth= { width: '100%' }
17 18
 const Item = Form.Item
18
-const initPoiData = POI_TYPES.map((poi) => poi.key).reduce((acc, key) => ({ ...acc, [key]: undefined }), {})
19 19
 
20 20
 const BuildingBasic = React.forwardRef((props, ref) => {
21
-  const [pois, setPois] = useState(initPoiData)
22 21
   const { form } = props;
23 22
   const { getFieldDecorator, getFieldValue, setFieldsValue } = form;
23
+
24
+  const [ loading, setLoading ] = useState({ arround: false })
25
+  const [
26
+    tags,
27
+    initTags,
28
+    updateTags,
29
+    deletePoi,
30
+    initPois,
31
+    pois2Tags,
32
+    // updatePois,
33
+    deleteTag,
34
+    updatePoisAndTags,
35
+  ] = useTags()
24 36
   
25 37
   // 视频文件上传前 回调
26 38
   const fileUploadBeforeUpload = (file, fileList) => {
@@ -44,22 +56,33 @@ const BuildingBasic = React.forwardRef((props, ref) => {
44 56
     }
45 57
 
46 58
     const lngLat = getFieldValue('coordinate').split(',')
47
-    let poisCopy = { ...pois }
48 59
 
49 60
     // 把支持的周边分类都去检索一遍
50
-    POI_TYPES.forEach(({ key }) => {
51
-      getPoiData({ type: key, location: lngLat, radius: e }).then(res => {
52
-        const poiStr = (res.pois || []).map((p) => p.name).join(',')
53
-        poisCopy = {
54
-          ...poisCopy,
55
-          [key]: poiStr
61
+    setLoading({ ...loading, arround: true })
62
+    Promise.all(POI_TYPES.map(({ key }) => getPoiData({ type: key, location: lngLat, radius: e }))).then((res) => {
63
+      const pois = POI_TYPES.reduce((acc, { key }, inx) => {
64
+        return {
65
+          ...acc,
66
+          [key]: (res[inx] || {}).pois || []
56 67
         }
57
-        setPois({ ...poisCopy })
58
-      }).catch(err => {
59
-        console.error(err)
60
-        notification.error({ message: '周边数据获取失败' })
61
-      })
68
+      }, {})
69
+
70
+      updatePoisAndTags(pois)
71
+      setLoading({ ...loading, arround: false })
72
+    }).catch((err) => {
73
+      setLoading({ ...loading, arround: false })
74
+      console.error(err)
75
+      notification.error({ message: '周边数据获取异常' })
62 76
     })
77
+
78
+    // POI_TYPES.forEach(({ key }) => {
79
+    //   getPoiData({ type: key, location: lngLat, radius: e }).then(res => {
80
+    //     updatePois(key, res.pois || [])
81
+    //   }).catch(err => {
82
+    //     console.error(err)
83
+    //     notification.error({ message: '周边数据获取失败' })
84
+    //   })
85
+    // })
63 86
   }
64 87
 
65 88
   return (
@@ -201,36 +224,56 @@ const BuildingBasic = React.forwardRef((props, ref) => {
201 224
           </Select>,
202 225
         )}
203 226
       </Form.Item>
204
-      <Form.Item label="周边交通" >
205
-        {getFieldDecorator('buildingTransport')(
206
-          <EditableArround pois={pois[POI_TYPES_KETY.Transport]} />,
207
-        )}
208
-      </Form.Item>
209
-      <Form.Item label="周边商业" >
210
-        {getFieldDecorator('buildingMall')(
211
-          <EditableArround pois={pois[POI_TYPES_KETY.Mall]} />,
212
-        )}
213
-      </Form.Item>
214
-      <Form.Item label="周边学校" >
215
-        {getFieldDecorator('buildingEdu')(
216
-          <EditableArround pois={pois[POI_TYPES_KETY.Edu]} />,
217
-        )}
218
-      </Form.Item>
219
-      <Form.Item label="周边医院" >
220
-        {getFieldDecorator('buildingHospital')(
221
-          <EditableArround pois={pois[POI_TYPES_KETY.Hospital]} />,
222
-        )}
223
-      </Form.Item>
224
-      <Form.Item label="周边银行" >
225
-        {getFieldDecorator('buildingBank')(
226
-          <EditableArround pois={pois[POI_TYPES_KETY.Bank]} />,
227
-        )}
228
-      </Form.Item>
229
-      <Form.Item label="周边餐饮" >
230
-        {getFieldDecorator('buildingRestaurant')(
231
-          <EditableArround pois={pois[POI_TYPES_KETY.Restaurant]} />,
232
-        )}
233
-      </Form.Item>
227
+      <Spin spinning={loading.arround}>
228
+        <Form.Item label="周边交通" >
229
+          <EditableArround
230
+            tags={tags.buildingTransport}
231
+            onChange={(e) => updateTags('buildingTransport', e)}
232
+            onDelete={(tag) => deleteTag('buildingTransport', tag)}
233
+            onPoiDelete={(poi) => deletePoi('Transport', poi)}
234
+          />
235
+        </Form.Item>
236
+        <Form.Item label="周边商业" >
237
+          <EditableArround
238
+            tags={tags.buildingMall}
239
+            onChange={(e) => updateTags('buildingMall', e)}
240
+            onDelete={(tag) => deleteTag('buildingMall', tag)}
241
+            onPoiDelete={(poi) => deletePoi('Mall', poi)}
242
+          />
243
+        </Form.Item>
244
+        <Form.Item label="周边学校" >
245
+          <EditableArround
246
+            tags={tags.buildingEdu}
247
+            onChange={(e) => updateTags('buildingEdu', e)}
248
+            onDelete={(tag) => deleteTag('buildingEdu', tag)}
249
+            onPoiDelete={(poi) => deletePoi('Edu', poi)}
250
+          />
251
+        </Form.Item>
252
+        <Form.Item label="周边医院" >
253
+          <EditableArround
254
+            tags={tags.buildingHospital}
255
+            onChange={(e) => updateTags('buildingHospital', e)}
256
+            onDelete={(tag) => deleteTag('buildingHospital', tag)}
257
+            onPoiDelete={(poi) => deletePoi('Hospital', poi)}
258
+          />
259
+        </Form.Item>
260
+        <Form.Item label="周边银行" >
261
+          <EditableArround
262
+            tags={tags.buildingBank}
263
+            onChange={(e) => updateTags('buildingBank', e)}
264
+            onDelete={(tag) => deleteTag('buildingBank', tag)}
265
+            onPoiDelete={(poi) => deletePoi('Bank', poi)}
266
+          />
267
+        </Form.Item>
268
+        <Form.Item label="周边餐饮" >
269
+          <EditableArround
270
+            tags={tags.buildingRestaurant}
271
+            onChange={(e) => updateTags('buildingRestaurant', e)}
272
+            onDelete={(tag) => deleteTag('buildingRestaurant', tag)}
273
+            onPoiDelete={(poi) => deletePoi('Restaurant', poi)}
274
+          />
275
+        </Form.Item>
276
+      </Spin>
234 277
       <FormGroupItem>
235 278
         <Form.Item label="绿化率" {...gourpItemLayout.ab.a } >
236 279
           {getFieldDecorator('greeningRate')(<Input />)}

+ 116
- 0
src/pages/building/Edit/Basic/tag.js 查看文件

@@ -0,0 +1,116 @@
1
+import { useCallback, useState, useRef } from "react";
2
+
3
+export default function useTags(form) {
4
+  const [tags, setTags] = useState({})
5
+  const poisRef = useRef({})
6
+
7
+  // const updatePoi2Form = pois => form.setFieldsValue('mapJson', Object.keys(pois).map((key) => ({ key, data: pois[key] })))
8
+
9
+  // initPois 必须先于 initTags 执行
10
+  // initPois, initTags 是在请求远程数据结束后执行
11
+  const initPois = useCallback((raw) => {
12
+    const newPois = !raw ? {} : raw.reduce((acc, poi) => {
13
+      const { key, data } = poi
14
+      return {
15
+        ...acc,
16
+        [key]: data || []
17
+      }
18
+    }, {})
19
+
20
+    poisRef.current = newPois
21
+  }, [])
22
+
23
+  // 主要用于地图搜索周边结果
24
+  const updatePois = useCallback((key, val) => {
25
+    poisRef.current[key] = val
26
+
27
+    // 先把 tags 中的 poi 去掉, 再更新
28
+    const tagKey = `building${key}`
29
+    const newTags = (tags[tagKey] || []).filter((tag) => !tag.id).concat(val)
30
+    setTags({
31
+      ...tags,
32
+      [tagKey]: newTags,
33
+    })
34
+
35
+    // 更新form
36
+    // updatePoi2Form(poisRef.current)
37
+    // form.setFieldsValue({ [key]: newTags.map((tag) => tag.name).join(',') })
38
+  }, [tags])
39
+
40
+  // 主要用于单个 poi 删除
41
+  const deletePoi = useCallback((key, val) => {
42
+    if (!poisRef.current[key] || !poisRef.current[key].length) return;
43
+
44
+    const newPois = poisRef.current[key].filter((poi) => poi.name !== val.name)
45
+    poisRef.current[key] = newPois
46
+
47
+    // 更新form
48
+    // updatePoi2Form(poisRef.current)
49
+  }, [])
50
+
51
+  const updatePoisAndTags = useCallback((pois) => {
52
+    poisRef.current = pois
53
+    const keys = Object.keys(pois)
54
+    
55
+    const newTags = keys.reduce((acc, key) => {
56
+      const tagKey = `building${key}`
57
+      const arr = (tags[tagKey] || []).filter((tag) => !tag.id).concat(pois[key])
58
+      return { ...acc, [tagKey]: arr }
59
+    }, tags)
60
+
61
+    setTags(newTags)
62
+  }, [tags])
63
+
64
+  const initTags = useCallback((key, raw) => {
65
+    const tagArr = !raw ? [] : raw.split(',').filter(Boolean)
66
+
67
+    if (!tagArr.length) return;
68
+
69
+    const pois = poisRef.current[key] || []
70
+
71
+    const newTags = tagArr.map((tag) => {
72
+      const found = pois.filter((poi) => poi.name === tag)[0]
73
+
74
+      return found || { name: tag }
75
+    })
76
+
77
+    setTags({
78
+      ...tags,
79
+      [key]: newTags,
80
+    })
81
+  }, [tags])
82
+
83
+  const updateTags = useCallback((key, newTags = []) => {
84
+    setTags({
85
+      ...tags,
86
+      [key]: newTags,
87
+    })
88
+
89
+    // form.setFieldsValue({ [key]: newTags.map((tag) => tag.name).join(',') })
90
+  }, [tags])
91
+
92
+  // 主要用户单个 tag 删除
93
+  const deleteTag = useCallback((key, tag) => {
94
+    if (!tags[key] || !tags[key].length) return;
95
+
96
+    const leftTags = tags[key].filter((x) => x.name !== tag.name)
97
+
98
+    setTags({
99
+      ...tags,
100
+      [key]: leftTags,
101
+    })
102
+
103
+    // form.setFieldsValue({ [key]: leftTags.map((tag) => tag.name).join(',') })
104
+  })
105
+
106
+  return [
107
+    tags,
108
+    initTags,
109
+    updateTags,
110
+    deletePoi,
111
+    initPois,
112
+    updatePois,
113
+    deleteTag,
114
+    updatePoisAndTags,
115
+  ]
116
+}

+ 41
- 0
src/pages/building/Edit/components/EditableArround/hooks/usePoi.js 查看文件

@@ -0,0 +1,41 @@
1
+import { useCallback, useEffect, useRef } from "react";
2
+
3
+export default function usePoi(pois, tags, setTags) {
4
+  const poiRef = useRef([])
5
+
6
+  useEffect(() => {
7
+    const len1 = !pois ? 0 : pois.length
8
+    const len2 = poiRef.current.length
9
+
10
+    // 空数据
11
+    if (len1 === len2 && len1 === 0) {
12
+      return
13
+    }
14
+
15
+    // 如果是 poiRef 更新导致了 pois 更新
16
+    if (len1 > 0 && poiRef.current === pois) {
17
+      return
18
+    }
19
+
20
+    // 如果是初始化
21
+    poiRef.current = pois || []
22
+  }, [pois])
23
+
24
+  const updatePois = useCallback((pois) => {
25
+    const newTags = tags.filter((tag) => !tag.id)
26
+    setTags(newTags.concat(pois))
27
+  }, [tags])
28
+
29
+  const deletePoi = useCallback((poi) => {
30
+    const newTags = tags.filter((tag) => tag.id !== poi.id)
31
+
32
+    const newPois = !poiRef.current.length ? [] : poiRef.current.filter((p) => p.id !== poi.id)
33
+    poiRef.current = newPois
34
+
35
+    setTags(newTags)
36
+
37
+    return newPois
38
+  }, [tags])
39
+
40
+  return [updatePois, deletePoi]
41
+}

+ 43
- 0
src/pages/building/Edit/components/EditableArround/hooks/useTag.js 查看文件

@@ -0,0 +1,43 @@
1
+import { useCallback, useRef } from "react";
2
+
3
+export default function useTag(inited, tags, setTags) {
4
+  const getNewTag = useRef()
5
+  getNewTag.current = (tag) => {
6
+    const matched = tags.filter((item) => (tag.name === item.name))[0]
7
+
8
+    if (matched && matched.id) {
9
+      return matched
10
+    }
11
+
12
+    return tag
13
+  }
14
+
15
+  const updateTags = useCallback((nwTags) => {
16
+    if (!nwTags || !nwTags.length) {
17
+      return tags
18
+    }
19
+
20
+    const nwVals = nwTags.map((tag) => getNewTag.current(tag))
21
+    setTags(nwVals)
22
+    inited.current = true
23
+  }, [tags])
24
+
25
+  const appendTags = useCallback((tag) => {
26
+    const found = tags.filter((item) => (tag.name === item.name))[0]
27
+    if (found) {
28
+      return
29
+    }
30
+
31
+    setTags(tags.concat(tag))
32
+    inited.current = true
33
+  }, [tags])
34
+
35
+
36
+  const deleteTags = useCallback((tag) => {
37
+    const nwVal = tags.filter((it) => it.name !== tag.name)
38
+    
39
+    setTags(nwVal)
40
+  }, [tags])
41
+
42
+  return [updateTags, appendTags, deleteTags]
43
+}

+ 17
- 0
src/pages/building/Edit/components/EditableArround/hooks/useValue.js 查看文件

@@ -0,0 +1,17 @@
1
+import { useEffect, useRef } from "react"
2
+
3
+export default function useValue(value, poiRef, updateTags) {
4
+  const valueInited = useRef(false)
5
+
6
+  useEffect(() => {
7
+    if (!valueInited.current && value) {
8
+      const vals = value.split(',').filter(Boolean).map((tag) => ({ name: tag }))
9
+      const newTags = !poiRef.length ? vals : vals.map((val) => {
10
+        const found = poiRef.current.filter((poi) => poi.name === val.name)[0]
11
+        return found || val
12
+      })
13
+      updateTags(newTags)
14
+      valueInited.current = true
15
+    }
16
+  }, [value])
17
+}

+ 10
- 38
src/pages/building/Edit/components/EditableArround/index.jsx 查看文件

@@ -3,66 +3,38 @@ import { Typography, Tag, Input, Icon } from 'antd'
3 3
 import MapPois from './MapPois'
4 4
 import { arrRemove, arrAdd } from './utils'
5 5
 
6
-/**
7
- * 因为周边TAG, 包含了2个部分的维护,一个是自定义周边,一个是地图周边,所以,需要单独写2个标签的处理
8
- */
9
-
10 6
 export default React.forwardRef((props, ref) => {
7
+  const { tags = [], onPoiDelete, onDelete, onChange } = props
8
+
11 9
   const inputRef = useRef()
12
-  const poiRef = useRef()
13 10
   const [inputValue, setInputValue] = useState()
14 11
   const [inputVisible, setInputVisible] = useState(false)
15
-  const [tags, setTags] = useState([])
16
-  const [mapTags, setMapTags] = useState([])
17
-
18
-  const handlePoiChange = (e) => {
19
-    setMapTags(e)
20
-
21
-    // 先清理原来的
22
-    const tagArr = arrRemove(tags, mapTags)
23
-    // 再加入新的
24
-    const nwTags = arrAdd(tagArr, e)
25
-
26
-    if (nwTags !== tags) {
27
-      setTags(nwTags)
28
-  
29
-      if (props.onChange) {
30
-        props.onChange(nwTags.join(','))
31
-      }
32
-    }
33
-  }
34 12
 
35 13
   const handleClose = (tag) => {
36
-    if (mapTags.indexOf(tag) > -1) {
37
-      setMapTags(mapTags.filter((it) => it !== tag))
14
+    if (tag.id && onPoiDelete) {
15
+      onPoiDelete(tag)
38 16
     }
39 17
 
40
-    if (props.onChange) {
41
-      props.onChange(tags.filter((t) => t !== tag).join(','))
18
+    if (onDelete) {
19
+      onDelete(tag)
42 20
     }
43 21
   }
44 22
 
45 23
   const handleInputConfirm = () => {
46
-    const exists = tags.indexOf(inputValue) > -1
24
+    const exists = tags.filter((tag) => tag.name === inputValue)[0]
47 25
     if (!exists) {
48
-      props.onChange(tags.concat(inputValue).join(','))
26
+      onChange(tags.concat({ name: inputValue }))
49 27
     }
50 28
     setInputVisible(false)
51 29
     setInputValue()
52 30
   }
53 31
 
54
-  useEffect(() => {
55
-    const arr = props.value ? props.value.split(',') : []
56
-    setTags(arr)
57
-  }, [props.value])
58
-
59 32
   return (
60 33
     <div>
61
-      <MapPois ref={poiRef} pois={props.pois} onChange={handlePoiChange} />
62 34
       {
63 35
         tags.map((tag) => (
64
-          <Tag key={tag} closable onClose={() => handleClose(tag)}>
65
-            <Typography.Text>{tag}</Typography.Text>
36
+          <Tag key={tag.name} closable onClose={() => handleClose(tag)}>
37
+            <Typography.Text>{tag.name}</Typography.Text>
66 38
           </Tag>
67 39
         ))
68 40
       }