张延森 il y a 4 ans
Parent
révision
7d40fd4036

+ 1
- 1
config/routes.js Voir le fichier

@@ -73,7 +73,7 @@ export default [
73 73
               {
74 74
                 path: 'buildingInfo',
75 75
                 name: '楼栋管理',
76
-                component: './property/building'
76
+                component: './property/building/list'
77 77
               },
78 78
               {
79 79
                 path: 'buildingInfo/importExcel',

+ 84
- 0
src/pages/property/building/components/Building.jsx Voir le fichier

@@ -0,0 +1,84 @@
1
+import React, { useEffect, useState } from 'react'
2
+import { Tabs, Spin, Button, Icon, Modal, notification } from 'antd'
3
+import { fetch, fetchList, apis } from '@/utils/request'
4
+import Prompt from '@/components/Prompt'
5
+import BuildingUnit from './BuildingUnit'
6
+
7
+const fetchBuildingList = fetch(apis.buildingOwnerInfo.getBuildingList)
8
+const addNode = fetch(apis.buildingOwnerInfo.addNode)
9
+const deleteNode = fetch(apis.buildingOwnerInfo.deleteNode)
10
+
11
+export default props => {
12
+  const [loading, setLoading] = useState(false)
13
+  const [buildingVisible, setBuildingVisible] = useState(false)
14
+  const [buildingList, setBuildingList] = useState([])
15
+
16
+  const handleAddBuilding = v => {
17
+    addNode({ data: {
18
+      id: props.phase.id,
19
+      name: props.phase.name,
20
+      type: 'building',
21
+      nodeNumber: v,
22
+    }}).then((res) => {
23
+      notification.success({message: '添加成功'})
24
+      setBuildingList(buildingList.concat(res))
25
+    })
26
+    setBuildingVisible(false)
27
+  }
28
+
29
+  const handleDeleteBuilding = building => {
30
+    Modal.confirm({
31
+      title: `确认删除 ${building.name} 数据?`,
32
+      content: '谨慎操作, 数据不可恢复',
33
+      onOk: () => {
34
+        deleteNode({ data: {
35
+          id: building.id,
36
+          name: building.name,
37
+          type: 'unit', // 删除的时候, type 是 unit 。这个接口比较诡异
38
+          nodeNumber: building.name,
39
+        }}).then(() => {
40
+          notification.success({message: '删除成功'})
41
+          setBuildingList(buildingList.filter(x => x.id !== building.id))
42
+        })
43
+
44
+      }
45
+    })
46
+  }
47
+
48
+  useEffect(() => {
49
+    if (props.phase && props.phase.id) {
50
+      setLoading(true)
51
+      fetchBuildingList({params: {phaseId: props.phase.id}}).then(res => {
52
+        setBuildingList(res)
53
+        setLoading(false)
54
+      }).catch(() => setLoading(false))
55
+    }
56
+  }, [props.phase])
57
+
58
+  return (
59
+    <div style={{marginTop: 8}}>
60
+      <Spin spinning={loading}>
61
+        <Tabs
62
+          defaultActiveKey="0"
63
+          type="line"
64
+          tabPosition="left"
65
+          tabBarExtraContent={<Button type="link" onClick={() => setBuildingVisible(true)}><Icon type="plus" />栋</Button>}
66
+        >
67
+        {
68
+          buildingList.map((building, index) => {
69
+            return (
70
+              <Tabs.TabPane
71
+                key={`${index}`}
72
+                tab={<span>{building.name} <Icon type="close" style={{fontSize: '0.8em', color: '#888', marginLeft: 8}} onClick={() => handleDeleteBuilding(building)} /></span>}
73
+              >
74
+                <BuildingUnit building={building} phase={props.phase} />
75
+              </Tabs.TabPane>
76
+            )
77
+          })
78
+        }
79
+        </Tabs>
80
+      </Spin>
81
+      <Prompt visible={buildingVisible} onOk={handleAddBuilding} onCancel={() => setBuildingVisible(false)} placeholder="请填写楼栋名称" />
82
+    </div>
83
+  )
84
+}

+ 96
- 0
src/pages/property/building/components/BuildingLevelCell.jsx Voir le fichier

@@ -0,0 +1,96 @@
1
+import React, { useEffect, useState } from 'react'
2
+import { Menu, Dropdown, Button, Popconfirm, Icon, notification } from 'antd'
3
+import Prompt from '@/components/Prompt'
4
+import { fetchList, fetch, apis } from '@/utils/request'
5
+
6
+const addNode = fetch(apis.buildingOwnerInfo.addNode)
7
+const updateNode = fetch(apis.buildingOwnerInfo.updateNode)
8
+const deleteNode = fetch(apis.buildingOwnerInfo.deleteNode)
9
+
10
+export default props => {
11
+  const [roomInfo, setRoomInfo] = useState({})
12
+  const [levelVisible, setLevelVisible] = useState(false)
13
+  const [houseVisible, setHouseVisible] = useState(false)
14
+
15
+  const handleEdit = val => {
16
+    updateNode({ data: {
17
+      id: roomInfo.levelId,
18
+      name: val,
19
+      type: 'level',
20
+    }}).then(() => {
21
+      const room = {
22
+        ...roomInfo,
23
+        levelName: val
24
+      }
25
+      if (props.onEdit) {
26
+        props.onEdit(room)
27
+      }
28
+      notification.success({message: '修改成功'})
29
+    })
30
+    setLevelVisible(false)
31
+  }
32
+
33
+  const handleDelete = () => {
34
+    deleteNode({ data: {
35
+      id: roomInfo.levelId,
36
+      name: roomInfo.levelName,
37
+      type: 'roomNo',   // 删除楼层传 roomNo
38
+      nodeNumber: undefined,
39
+    }}).then(() => {
40
+      if (props.onDelete) {
41
+        props.onDelete(roomInfo)
42
+      }
43
+      notification.success({message: '删除成功'})
44
+    })
45
+  }
46
+
47
+  const handleHouseAdd = val => {
48
+    addNode({ data: {
49
+      id: roomInfo.levelId,
50
+      name: roomInfo.levelName,
51
+      type: 'roomNo',
52
+      nodeNumber: val,
53
+    }}).then((res) => {
54
+      if (props.onHouseAdd) {
55
+        props.onHouseAdd(res)
56
+      }
57
+      notification.success({message: '添加成功'})
58
+    })
59
+    setHouseVisible(false)
60
+  }
61
+
62
+  const menu = (
63
+    <Menu>
64
+      <Menu.Item>
65
+        <Button type="link" onClick={() => setHouseVisible(true)}><Icon type="plus" />房</Button>
66
+      </Menu.Item>
67
+      <Menu.Item>
68
+        <Button type="link" onClick={() => setLevelVisible(true)}>编辑</Button>
69
+      </Menu.Item>
70
+      <Menu.Item>
71
+        <Popconfirm
72
+         title={`确认删除 ${roomInfo.levelName}`}
73
+         onConfirm={handleDelete}
74
+         okText="是"
75
+         cancelText="否"
76
+        >
77
+          <Button type="danger" ghost style={{border: 'none', boxShadow: 'none'}}>删除</Button>
78
+        </Popconfirm>
79
+      </Menu.Item>
80
+    </Menu>
81
+  );
82
+  
83
+  useEffect(() => {
84
+    setRoomInfo(props.dataSource || {})
85
+  }, [props.dataSource])
86
+
87
+  return (
88
+    <span>
89
+      <Dropdown overlay={menu} trigger={['contextMenu']} placement="topCenter">
90
+        <div style={{cursor: 'pointer'}}>{roomInfo.levelName}</div>
91
+      </Dropdown>
92
+      <Prompt visible={levelVisible} onOk={handleEdit} onCancel={() => setLevelVisible(false)} placeholder="请填写楼层名称" />
93
+      <Prompt visible={houseVisible} onOk={handleHouseAdd} onCancel={() => setHouseVisible(false)} placeholder="请填写房室名称" />
94
+    </span>
95
+  )
96
+}

+ 111
- 0
src/pages/property/building/components/BuildingLevelRow.jsx Voir le fichier

@@ -0,0 +1,111 @@
1
+import React, { useEffect, useMemo, useRef, useState } from 'react'
2
+import { Table, Spin, Menu, Dropdown, Button, Popconfirm, Icon, notification } from 'antd'
3
+import { maxBy } from 'lodash'
4
+import Prompt from '@/components/Prompt'
5
+import { fetchList, fetch, apis } from '@/utils/request'
6
+import BuildingRoomCell from './BuildingRoomCell'
7
+import BuildingLevelCell from './BuildingLevelCell'
8
+import BuildingUnitCell from './BuildingUnitCell'
9
+
10
+const addNode = fetch(apis.buildingOwnerInfo.addNode)
11
+const updateNode = fetch(apis.buildingOwnerInfo.updateNode)
12
+const deleteNode = fetch(apis.buildingOwnerInfo.deleteNode)
13
+
14
+const nvlIdStart = 'tmp'
15
+
16
+export default props => {
17
+  const [dataList, setDataList] = useState([])
18
+  const [roomNum, setRoomNum] = useState(0)
19
+
20
+  // 表格列
21
+  const columns = useMemo(() => {
22
+    const rooms = new Array(roomNum).fill()
23
+
24
+    return [
25
+      // 楼层字段
26
+      {
27
+        title: '#',
28
+        dataIndex: 'levelName',
29
+        key: 'levelName',
30
+        width: 80,
31
+        align: 'center',
32
+        render: (t, row) => (
33
+          <BuildingLevelCell
34
+            dataSource={row}
35
+            onHouseAdd={props.onHouseAdd}
36
+            onEdit={props.onLevelEdit}
37
+            onDelete={props.onLevelDelete}
38
+          />
39
+        )
40
+      },
41
+      ...rooms.map((_, index) => ({
42
+        title: `房间 ${index + 1}`,
43
+        dataIndex: `name-${index}`,
44
+        key: `name-${index}`,
45
+        align: 'center',      
46
+        render: (t, row) => (
47
+          <BuildingRoomCell
48
+            dataSource={{
49
+              ...row,
50
+              id: row[`id-${index}`],
51
+              name: row[`name-${index}`]
52
+            }}
53
+            onEdit={props.onHouseEdit}
54
+            onDelete={props.onHouseDelete}
55
+          />
56
+        )
57
+      }))
58
+    ]
59
+  }, [roomNum, props])
60
+  
61
+  useEffect(() => {
62
+    const list = (props.dataSource || []).slice()
63
+    if (!list.length) {
64
+      return
65
+    }
66
+
67
+    // 找到 房间最多的那层, 然后所有的楼层都按照这个房间数造数据
68
+    const maxRooms = maxBy(list, x => x.length)
69
+    setRoomNum(maxRooms.length)
70
+
71
+    // 合并同楼层的房间
72
+    const zipedList = list.map(rooms => {
73
+      return maxRooms.reduce((acc, _, index) => {
74
+        const room = rooms[index] || {}
75
+
76
+        return {
77
+          ...acc,
78
+          ...room,
79
+          id: acc.id || room.id || `${nvlIdStart}-${Math.random().toString(36).substring(2)}`,
80
+          [`id-${index}`]: room.id,
81
+          [`name-${index}`]: room.name || '#'
82
+        }
83
+      }, {})
84
+    })
85
+
86
+    setDataList(zipedList)
87
+  }, [props.dataSource])
88
+
89
+  // 任意一个房间数据都可以
90
+  // 因为所有房间的单元信息都是一样的
91
+  const anyRoom = dataList[0]
92
+
93
+  return (
94
+    <div>
95
+      <BuildingUnitCell
96
+        dataSource={anyRoom}
97
+        onLevelAdd={props.onLevelAdd}
98
+        onEdit={props.onUnitEdit}
99
+        onDelete={props.onUnitDelete}
100
+      />
101
+      <Table
102
+        columns={columns}
103
+        dataSource={dataList}
104
+        pagination={false}
105
+        rowKey="id"
106
+        size="middle"
107
+        bordered
108
+      />
109
+    </div>
110
+  )
111
+}

+ 95
- 0
src/pages/property/building/components/BuildingRoomCell.jsx Voir le fichier

@@ -0,0 +1,95 @@
1
+import React, { useEffect, useState } from 'react'
2
+import { Menu, Dropdown, Button, Popconfirm, Icon, notification } from 'antd'
3
+import Prompt from '@/components/Prompt'
4
+import { fetchList, fetch, apis } from '@/utils/request'
5
+
6
+const addNode = fetch(apis.buildingOwnerInfo.addNode)
7
+const updateNode = fetch(apis.buildingOwnerInfo.updateNode)
8
+const deleteNode = fetch(apis.buildingOwnerInfo.deleteNode)
9
+
10
+export default props => {
11
+  const [houseVisible, setHouseVisible] = useState(false)
12
+  const [roomInfo, setRoomInfo] = useState({})
13
+
14
+  const handleDelete = () => {
15
+    deleteNode({ data: {
16
+      id: roomInfo.id,
17
+      name: roomInfo.name,
18
+      type: 'end',   // 删除房间传 end
19
+      nodeNumber: undefined,
20
+    }}).then(() => {
21
+      if (props.onDelete) {
22
+        props.onDelete(roomInfo)
23
+      }
24
+      notification.success({message: '删除成功'})
25
+    })
26
+  }
27
+
28
+  const handleEdit = val => {
29
+    if (!roomInfo.id) {
30
+      addNode({ data: {
31
+        id: roomInfo.levelId,
32
+        name: roomInfo.levelName,
33
+        type: 'roomNo',
34
+        nodeNumber: val,
35
+      }}).then((res) => {
36
+        if (props.onEdit) {
37
+          props.onEdit(res)
38
+        }
39
+        notification.success({message: '修改成功'})
40
+      })
41
+    } else {
42
+      updateNode({ data: {
43
+        id: roomInfo.id,
44
+        name: val,
45
+        type: 'roomNo',
46
+      }}).then(() => {
47
+        const room = {
48
+          ...roomInfo,
49
+          name: val
50
+        }
51
+
52
+        if (props.onEdit) {
53
+          props.onEdit(room)
54
+        }
55
+        notification.success({message: '修改成功'})
56
+      })
57
+    }
58
+    setHouseVisible(false)
59
+  }
60
+
61
+  const menu = (
62
+    <Menu>
63
+      <Menu.Item>
64
+        <Button type="link" onClick={() => setHouseVisible(true)}>编辑</Button>
65
+      </Menu.Item>
66
+      {
67
+        roomInfo.id && (
68
+          <Menu.Item>
69
+            <Popconfirm
70
+             title={`确认删除 ${roomInfo.name} ?`}
71
+             onConfirm={handleDelete}
72
+             okText="是"
73
+             cancelText="否"
74
+            >
75
+              <Button type="danger" ghost style={{border: 'none', boxShadow: 'none'}}>删除</Button>
76
+            </Popconfirm>
77
+          </Menu.Item>
78
+        )
79
+      }
80
+    </Menu>
81
+  );
82
+
83
+  useEffect(() => {
84
+    setRoomInfo(props.dataSource || {})
85
+  }, [props.dataSource])
86
+
87
+  return (
88
+    <span>
89
+      <Dropdown overlay={menu} trigger={['contextMenu']} placement="topCenter">
90
+        <div style={{cursor: 'pointer'}}>{roomInfo.name}</div>
91
+      </Dropdown>
92
+      <Prompt visible={houseVisible} onOk={handleEdit} onCancel={() => setHouseVisible(false)} placeholder="请填写房室名称" />
93
+    </span>
94
+  )
95
+}

+ 234
- 0
src/pages/property/building/components/BuildingUnit.jsx Voir le fichier

@@ -0,0 +1,234 @@
1
+import React, { useEffect, useMemo, useState, useCallback } from 'react'
2
+import { Table, Spin, Button, Icon, notification } from 'antd'
3
+import { fetchList, fetch, apis } from '@/utils/request'
4
+import { groupBy, orderBy } from 'lodash'
5
+import Prompt from '@/components/Prompt'
6
+import BuildingLevelRow from './BuildingLevelRow'
7
+
8
+const fetchBuildingList = fetchList(apis.buildingOwnerInfo.getAllRoomNoList)
9
+const addNode = fetch(apis.buildingOwnerInfo.addNode)
10
+const deleteNode = fetch(apis.buildingOwnerInfo.deleteNode)
11
+
12
+const getNumFromStr = str => {
13
+  return ((str || '').match(/\d+/g) || []).join('') - 0
14
+}
15
+
16
+/**
17
+ * 房间排序规则
18
+ * 1、维护内容必须有数字
19
+ * 2、单元 从小到大
20
+ * 3、楼层 从大到小
21
+ * 4、房号 从小到大
22
+ * @param {*} room 
23
+ */
24
+const sortRoomList = roomList => {
25
+  return orderBy(roomList,
26
+    [
27
+      room => getNumFromStr(room.unitName),
28
+      room => getNumFromStr(room.levelName),
29
+      room => getNumFromStr(room.name),
30
+    ],
31
+    [
32
+      'asc',
33
+      'desc',
34
+      'asc'
35
+    ]
36
+  )
37
+}
38
+
39
+export default props => {
40
+  const [loading, setLoading] = useState(false)
41
+  const [unitVisible, setUnitVisible] = useState(false)
42
+  const [roomList, setRoomList] = useState([])
43
+  const [roomGroup, setRoomGroup] = useState({})
44
+  
45
+  const handleAddUnit = v => {
46
+    addNode({ data: {
47
+      id: props.building.id,
48
+      name: props.building.name,
49
+      type: 'unit',
50
+      nodeNumber: v,
51
+    }}).then((res) => {
52
+      // 构造房间数据
53
+      const roomInfo = {
54
+        ...res,
55
+        unitId: res.id,
56
+        unitName: res.name,
57
+        id: undefined,
58
+        name: undefined,
59
+        levelId: undefined,
60
+        levelName: undefined,
61
+      }
62
+
63
+      setRoomList(roomList.concat(roomInfo))
64
+      notification.success({message: '添加成功'})
65
+    })
66
+    setUnitVisible(false)
67
+  }
68
+
69
+  const handleDeleteUnit = room => {
70
+    setRoomList(roomList.filter(r => r.unitId !== room.unitId))
71
+  }
72
+
73
+  const addHouse = useCallback(
74
+    room => {
75
+      // 如果有空房间, 直接替换空房间,否则新增一条记录
76
+      let nvlRoom = false
77
+  
78
+      let newList = roomList.map(item => {
79
+        if (room.phaseId === item.phaseId &&
80
+          room.buildingId === item.buildingId &&
81
+          room.unitId === item.unitId &&
82
+          room.levelId === item.levelId &&
83
+          !item.id && !nvlRoom) {
84
+            nvlRoom = true
85
+            return room
86
+          } else {
87
+            return item
88
+          }
89
+      })
90
+  
91
+      if (!nvlRoom) {
92
+        newList = newList.concat(room)
93
+      }
94
+  
95
+      setRoomList(sortRoomList(newList))
96
+    },
97
+    [roomList],
98
+  )
99
+
100
+  const handleHouseAdd = room => {
101
+    addHouse(room)
102
+  }
103
+
104
+  // 如果编辑 # 的房间, 实际上是新增房间
105
+  const handleHouseEdit = room => {
106
+    let edited = false
107
+    const newList = roomList.map(r => {
108
+      if (r.id === room.id) {
109
+        edited = true
110
+        return room
111
+      } else {
112
+        return r
113
+      }
114
+    })
115
+
116
+    if (edited) {
117
+      setRoomList(newList)
118
+    } else {
119
+      addHouse(room)
120
+    }
121
+  }
122
+
123
+  const handleHouseDelete = room => {
124
+    let newList = []
125
+    // 如果本层只有一个房间, 那么替换为空, 不删除数据
126
+    const sameLevelRooms = roomList.filter(r => r.levelId === room.levelId)
127
+    if (sameLevelRooms.length === 1) {
128
+      newList = roomList.map(item => {
129
+        if (item.id === room.id) {
130
+          return {
131
+            ...room,
132
+            id: undefined,
133
+            name: undefined,
134
+          }
135
+        } else {
136
+          return item
137
+        }
138
+      })
139
+    } else {
140
+      newList = roomList.filter(r => r.id !== room.id)
141
+    }
142
+    setRoomList(newList)
143
+  }
144
+
145
+  const handleLevelAdd = room => {
146
+    // 如果有空房间,替换空房间
147
+    let nvlRoom = false
148
+  
149
+    let newList = roomList.map(item => {
150
+      if (room.phaseId === item.phaseId &&
151
+        room.buildingId === item.buildingId &&
152
+        room.unitId === item.unitId &&
153
+        !item.levelId && !nvlRoom) {
154
+          nvlRoom = true
155
+          return room
156
+        } else {
157
+          return item
158
+        }
159
+    })
160
+
161
+    if (!nvlRoom) {
162
+      newList = newList.concat(room)
163
+    }
164
+
165
+    setRoomList(sortRoomList(newList))
166
+  }
167
+
168
+  const handleLevelEdit = room => {
169
+    setRoomList(roomList.map(item => {
170
+      return item.levelId === room.levelId ?
171
+        {...item, levelName: room.levelName} :
172
+        item
173
+    }))
174
+  }
175
+
176
+  const handleLevelDelete = room => {
177
+    setRoomList(roomList.filter(r => r.levelId !== room.levelId))
178
+  }
179
+
180
+  useEffect(() => {
181
+    if (props.building && props.phase && props.building.id && props.phase.id) {
182
+      setLoading(true)
183
+      fetchBuildingList({pageNum: 1, pageSize: 9999, phaseId: props.phase.id, buildingId: props.building.id}).then(res => {
184
+        const [list = []] = res || []
185
+        setRoomList(sortRoomList(list))
186
+        setLoading(false)
187
+      }).catch(e => console.error(e) || setLoading(false))
188
+    }
189
+  }, [props.building, props.phase])
190
+
191
+  useEffect(() => {
192
+    const groupByUnit = groupBy(roomList, row => [row.unitId, row.unitName].join(','))
193
+    const groupByLevel = Object.keys(groupByUnit).reduce((acc, key) => {
194
+      const groupWithLevelKey = groupBy(groupByUnit[key], row => [row.levelId, row.levelName].join(','))
195
+      const pureLevelArr = Object.keys(groupWithLevelKey).map(k => groupWithLevelKey[k])
196
+
197
+      return {
198
+        ...acc,
199
+        [key]: pureLevelArr
200
+      }
201
+    }, {})
202
+
203
+    setRoomGroup(groupByLevel)
204
+  }, [roomList])
205
+
206
+  return (
207
+    <div>
208
+      <Button style={{marginLeft: 24}} onClick={() => setUnitVisible(true)}><Icon type="plus" />单元</Button>
209
+      <Spin spinning={loading}>
210
+        <div style={{display: 'flex', overflowX: 'auto'}}>
211
+        {
212
+          Object.keys(roomGroup).map(key => {
213
+            return (
214
+              <div style={{flex: 'none', marginLeft: 48}} key={key}>
215
+                <BuildingLevelRow
216
+                  dataSource={roomGroup[key]}
217
+                  onUnitDelete={handleDeleteUnit}
218
+                  onHouseAdd={handleHouseAdd}
219
+                  onHouseEdit={handleHouseEdit}
220
+                  onHouseDelete={handleHouseDelete}
221
+                  onLevelAdd={handleLevelAdd}
222
+                  onLevelEdit={handleLevelEdit}
223
+                  onLevelDelete={handleLevelDelete}
224
+                />
225
+              </div>
226
+            )
227
+          })
228
+        }
229
+        </div>
230
+      </Spin>
231
+      <Prompt visible={unitVisible} onOk={handleAddUnit} onCancel={() => setUnitVisible(false)} placeholder="请填写单元名称" />
232
+    </div>
233
+  )
234
+}

+ 106
- 0
src/pages/property/building/components/BuildingUnitCell.jsx Voir le fichier

@@ -0,0 +1,106 @@
1
+import React, { useEffect, useState } from 'react'
2
+import { Menu, Dropdown, Button, Popconfirm, Icon, notification } from 'antd'
3
+import Prompt from '@/components/Prompt'
4
+import { fetchList, fetch, apis } from '@/utils/request'
5
+
6
+const addNode = fetch(apis.buildingOwnerInfo.addNode)
7
+const updateNode = fetch(apis.buildingOwnerInfo.updateNode)
8
+const deleteNode = fetch(apis.buildingOwnerInfo.deleteNode)
9
+
10
+export default props => {
11
+  const [roomInfo, setRoomInfo] = useState({})
12
+  const [unitVisible, setUnitVisible] = useState(false)
13
+  const [levelVisible, setLevelVisible] = useState(false)
14
+  
15
+  const handleEdit = val => {
16
+    updateNode({ data: {
17
+      id: roomInfo.unitId,
18
+      name: val,
19
+      type: 'unit',
20
+    }}).then(() => {
21
+      const room = {
22
+        ...roomInfo,
23
+        unitName: val
24
+      }
25
+      if (props.onEdit) {
26
+        props.onEdit(room)
27
+      }
28
+      notification.success({message: '修改成功'})
29
+    })
30
+    setUnitVisible(false)
31
+  }
32
+
33
+  const handleDelete = () => {
34
+    deleteNode({ data: {
35
+      id: roomInfo.unitId,
36
+      name: roomInfo.unitName,
37
+      type: 'level',   // 删除单元传 level
38
+      nodeNumber: undefined,
39
+    }}).then(() => {
40
+      if (props.onDelete) {
41
+        props.onDelete(roomInfo)
42
+      }
43
+      notification.success({message: '删除成功'})
44
+    })
45
+  }
46
+
47
+  const handleLevelAdd = val => {
48
+    addNode({ data: {
49
+      id: roomInfo.unitId,
50
+      name: roomInfo.unitName,
51
+      type: 'level',
52
+      nodeNumber: val,
53
+    }}).then((res) => {
54
+      // 构造房间数据
55
+      const room = {
56
+        ...res,
57
+        levelId: res.id,
58
+        levelName: res.name,
59
+        id: undefined,
60
+        name: undefined,
61
+      }
62
+
63
+      if (props.onLevelAdd) {
64
+        props.onLevelAdd(room)
65
+      }
66
+      notification.success({message: '添加成功'})
67
+    })
68
+
69
+    setLevelVisible(false)
70
+  }
71
+
72
+  useEffect(() => {
73
+    setRoomInfo(props.dataSource || {})
74
+  }, [props.dataSource])
75
+
76
+  const menu = (
77
+    <Menu>
78
+      <Menu.Item>
79
+        <Button type="link" onClick={() => setLevelVisible(true)}><Icon type="plus"/>层</Button>
80
+      </Menu.Item>
81
+      <Menu.Item>
82
+        <Button type="link" onClick={() => setUnitVisible(true)}>编辑</Button>
83
+      </Menu.Item>
84
+      <Menu.Item>
85
+        <Popconfirm
86
+         title={`确认删除 ${roomInfo.unitName} ?`}
87
+         onConfirm={handleDelete}
88
+         okText="是"
89
+         cancelText="否"
90
+        >
91
+          <Button type="danger" ghost style={{border: 'none', boxShadow: 'none'}}>删除</Button>
92
+        </Popconfirm>
93
+      </Menu.Item>
94
+    </Menu>
95
+  )
96
+
97
+  return (
98
+    <div style={{textAlign: 'center', fontSize: '1.2em', lineHeight: '3.5em'}} >
99
+      <Dropdown overlay={menu} trigger={['contextMenu']} placement="topCenter">
100
+        <div style={{cursor: 'pointer'}}>{roomInfo.unitName}</div>
101
+      </Dropdown>
102
+      <Prompt visible={unitVisible} onOk={handleEdit} onCancel={() => setUnitVisible(false)} placeholder="请填写单元名称" />
103
+      <Prompt visible={levelVisible} onOk={handleLevelAdd} onCancel={() => setLevelVisible(false)} placeholder="请填写楼层名称" />
104
+    </div>
105
+  )
106
+}

+ 95
- 0
src/pages/property/building/list.jsx Voir le fichier

@@ -0,0 +1,95 @@
1
+import React, { useEffect, useState } from 'react'
2
+import { PageHeader, Tabs, Spin, Button, Icon, Modal, notification } from 'antd'
3
+import router from 'umi/router'
4
+import { fetch, fetchList, apis } from '@/utils/request'
5
+import Prompt from '@/components/Prompt'
6
+import Building from './components/Building'
7
+
8
+const fetchPhaseList = fetch(apis.buildingOwnerInfo.getPhaseList)
9
+const addNode = fetch(apis.buildingOwnerInfo.addNode)
10
+const deleteNode = fetch(apis.buildingOwnerInfo.deleteNode)
11
+
12
+export default props => {
13
+  const [phaseList, setPhaseList] = useState([])
14
+  const [loading, setLoading] = useState(false)
15
+  const [phaseVisible, setPhaseVisible] = useState(false)
16
+
17
+  const handleEdit = (key, act) => {
18
+    const phase = phaseList[key - 0]
19
+
20
+    if (act === 'remove') {
21
+      Modal.confirm({
22
+        title: `确认删除 ${phase.name} 数据?`,
23
+        content: '谨慎操作, 数据不可恢复',
24
+        onOk: () => {
25
+          deleteNode({ data: {
26
+            id: phase.id,
27
+            name: phase.name,
28
+            type: 'building', // 删除的时候, type 是 building 。这个接口比较诡异
29
+            nodeNumber: phase.name,
30
+          }}).then(() => {
31
+            notification.success({message: '删除成功'})
32
+            setPhaseList(phaseList.filter(x => x.id !== phase.id))
33
+          })
34
+
35
+        }
36
+      })
37
+    }
38
+  }
39
+
40
+  const handleBatchImport = () => {
41
+    router.push('/property/buildingInfo/importExcel')
42
+  }
43
+
44
+  const handleAddPhase = v => {
45
+    addNode({ data: {
46
+      type: 'phase',
47
+      nodeNumber: v,
48
+    }}).then((res) => {
49
+      notification.success({message: '添加成功'})
50
+      setPhaseList(phaseList.concat(res))
51
+    })
52
+    setPhaseVisible(false)
53
+  }
54
+
55
+  // 获取期
56
+  useEffect(() => {
57
+    setLoading(true)
58
+    fetchPhaseList().then(res => {
59
+      setPhaseList(res)
60
+      setLoading(false)
61
+    }).catch(() => setLoading(false))
62
+  }, [])
63
+
64
+  return (
65
+    <div>
66
+      <PageHeader
67
+        title="楼栋管理"
68
+        backIcon={false}
69
+        extra={<Button type="link" onClick={handleBatchImport}><Icon type="upload"/>批量导入</Button>}
70
+      />
71
+      <div style={{marginTop: 16}}>
72
+        <Spin spinning={loading}>
73
+          <Tabs
74
+            defaultActiveKey="0"
75
+            type="editable-card"
76
+            onEdit={handleEdit}
77
+            tabBarExtraContent={<Button type="link" onClick={() => setPhaseVisible(true)}><Icon type="plus" />期</Button>}
78
+            hideAdd={true}
79
+            >
80
+          {
81
+            phaseList.map((phase, index) => {
82
+              return (
83
+                <Tabs.TabPane tab={phase.name} key={`${index}`}>
84
+                  <Building phase={phase} />
85
+                </Tabs.TabPane>
86
+              )
87
+            })
88
+          }
89
+          </Tabs>
90
+        </Spin>
91
+      </div>
92
+      <Prompt visible={phaseVisible} onOk={handleAddPhase} onCancel={() => setPhaseVisible(false)} placeholder="请填写期数名称" />
93
+    </div>
94
+  )
95
+}

+ 11
- 0
src/pages/property/proprietor/Add.jsx Voir le fichier

@@ -182,6 +182,17 @@ export default Form.create()(props => {
182 182
             props.form.getFieldDecorator('ownerName')(<Input />)
183 183
           }
184 184
         </Form.Item>
185
+        <Form.Item label="身份证号">
186
+          {
187
+            props.form.getFieldDecorator('idCard', {
188
+              rules: [
189
+                {
190
+                  len: 18, message: '身份证号长度为18位'
191
+                }
192
+              ]
193
+            })(<Input />)
194
+          }
195
+        </Form.Item>
185 196
         <Form.Item label="性别">
186 197
           {
187 198
             props.form.getFieldDecorator('gender')(

+ 1
- 1
src/pages/property/proprietor/index.jsx Voir le fichier

@@ -271,7 +271,7 @@ export default props => {
271 271
         />
272 272
         <Table.Column title="手机号" dataIndex="ownerTel" key="ownerTel" />
273 273
         <Table.Column title="身份证" dataIndex="idCard" key="idCard" />
274
-        <Table.Column title="性别" dataIndex="idCard" key="idCard1" render={t => (t.substring(-2, 1) - 0) % 2 === 1 ? '男' : '女'} />
274
+        <Table.Column title="性别" dataIndex="idCard" key="idCard1" render={t => !t ? '' : (t.substring(-2, 1) - 0) % 2 === 1 ? '男' : '女'} />
275 275
         <Table.Column
276 276
           title="房间号"
277 277
           dataIndex="roomNoName"

+ 14
- 0
src/services/buildingOwnerInfo_api.js Voir le fichier

@@ -44,6 +44,13 @@ export default prefix => {
44 44
       action: `admin.${moduleName}.roomno`
45 45
     },
46 46
 
47
+    //  查询 楼栋/单元/楼层/户号
48
+    getAllRoomNoList: {
49
+      url: `${prefix}/${moduleName}/building-room/list`,
50
+      method: 'get',
51
+      action: `admin.${moduleName}.all-room`
52
+    },
53
+
47 54
     // 获取楼栋户主信息列表
48 55
     buildingList: {
49 56
       url: `${prefix}/${moduleName}/list`,
@@ -135,6 +142,13 @@ export default prefix => {
135 142
       action: `admin.${moduleName}.addnode`
136 143
     },
137 144
 
145
+    // 编辑当前期/栋/单元/层/户/节点
146
+    updateNode: {
147
+      url: `${prefix}/${moduleName}/updatenode`,
148
+      method: 'post',
149
+      action: `admin.${moduleName}.updatenode`
150
+    },
151
+
138 152
     // 删除当前期/栋/单元/层/户/节点
139 153
     deleteNode: {
140 154
       url: `${prefix}/${moduleName}/deletenode`,